Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package email
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type resendTransport struct {
|
||||
apiKey string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func newResendTransport(apiKey string, client *http.Client) *resendTransport {
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: 20 * time.Second}
|
||||
}
|
||||
return &resendTransport{apiKey: apiKey, client: client}
|
||||
}
|
||||
|
||||
func (r *resendTransport) Name() string { return ProviderResend }
|
||||
|
||||
func (r *resendTransport) Send(ctx context.Context, from FromIdentity, msg Outbound) error {
|
||||
to := strings.TrimSpace(msg.To)
|
||||
if to == "" {
|
||||
return ErrInvalidRecipient
|
||||
}
|
||||
payload := map[string]any{
|
||||
"from": from.Formatted(),
|
||||
"to": []string{to},
|
||||
"subject": msg.Subject,
|
||||
}
|
||||
if strings.TrimSpace(msg.HTML) != "" {
|
||||
payload["html"] = msg.HTML
|
||||
}
|
||||
if strings.TrimSpace(msg.Text) != "" {
|
||||
payload["text"] = msg.Text
|
||||
}
|
||||
if strings.TrimSpace(from.ReplyTo) != "" {
|
||||
payload["reply_to"] = from.ReplyTo
|
||||
}
|
||||
if len(msg.Headers) > 0 {
|
||||
payload["headers"] = msg.Headers
|
||||
}
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.resend.com/emails", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+r.apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
res, err := r.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resend request failed")
|
||||
}
|
||||
defer res.Body.Close()
|
||||
raw, _ := io.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
if res.StatusCode >= 300 {
|
||||
return fmt.Errorf("resend api %d", res.StatusCode)
|
||||
}
|
||||
_ = raw
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyResendDomain checks the API key can list domains and that domain appears verified.
|
||||
// When Resend returns no domains (sandbox), returns ok=false with a clear message.
|
||||
func verifyResendDomain(ctx context.Context, apiKey, domain string, client *http.Client) (bool, string, error) {
|
||||
domain = strings.ToLower(strings.TrimSpace(domain))
|
||||
if domain == "" {
|
||||
return false, "domain required", nil
|
||||
}
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: 15 * time.Second}
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.resend.com/domains", nil)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("resend domains request failed")
|
||||
}
|
||||
defer res.Body.Close()
|
||||
raw, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if res.StatusCode == http.StatusUnauthorized {
|
||||
return false, "invalid Resend API key", nil
|
||||
}
|
||||
if res.StatusCode >= 300 {
|
||||
return false, fmt.Sprintf("resend domains api %d", res.StatusCode), nil
|
||||
}
|
||||
var parsed struct {
|
||||
Data []struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &parsed); err != nil {
|
||||
return false, "unexpected resend response", nil
|
||||
}
|
||||
for _, d := range parsed.Data {
|
||||
if strings.EqualFold(d.Name, domain) {
|
||||
st := strings.ToLower(strings.TrimSpace(d.Status))
|
||||
if st == "verified" || st == "ok" || st == "active" {
|
||||
return true, "domain verified with Resend", nil
|
||||
}
|
||||
return false, fmt.Sprintf("Resend domain status is %q", d.Status), nil
|
||||
}
|
||||
}
|
||||
return false, "domain not found in Resend account — add and verify it in the Resend dashboard", nil
|
||||
}
|
||||
Reference in New Issue
Block a user