package email import ( "bytes" "context" "encoding/json" "io" "net/http" "strconv" "time" "git.sendico.io/tech/sxchnge/notifications/model" ) func (c *client) post(ctx context.Context, key string, payload any) Outcome { body, err := json.Marshal(payload) if err != nil { return Outcome{State: model.Failed, Code: "invalid_payload"} } req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint, bytes.NewReader(body)) if err != nil { return Outcome{State: model.Failed, Code: "invalid_provider_config"} } req.Header.Set("Authorization", "Bearer "+c.key) req.Header.Set("Content-Type", "application/json") if key != "" { req.Header.Set("Idempotency-Key", key) } resp, err := c.http.Do(req) if err != nil { return Outcome{State: model.Unknown, Code: "provider_outcome_unknown"} } defer resp.Body.Close() // Discard a bounded response without logging provider-controlled content. _, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 64<<10)) switch { case resp.StatusCode >= 200 && resp.StatusCode < 300: return Outcome{State: model.Accepted} case resp.StatusCode == 429: delay := time.Minute if n, e := strconv.Atoi(resp.Header.Get("Retry-After")); e == nil && n > 0 && n <= 86400 { delay = time.Duration(n) * time.Second } else if date, e := http.ParseTime(resp.Header.Get("Retry-After")); e == nil && time.Until(date) > 0 { delay = time.Until(date) } return Outcome{State: model.RetryWait, Code: "provider_rate_limited", RetryAfter: delay} case resp.StatusCode >= 500 || resp.StatusCode == 408: return Outcome{State: model.Unknown, Code: "provider_outcome_unknown"} case resp.StatusCode == 401 || resp.StatusCode == 403: return Outcome{State: model.Failed, Code: "provider_authentication_failed"} default: return Outcome{State: model.Failed, Code: "provider_rejected"} } }