我们如何向 SendRec 添加通用 Webhooks

发布: (2026年2月22日 GMT+8 01:21)
7 分钟阅读
原文: Dev.to

Source: Dev.to

(请提供需要翻译的正文内容,我才能为您完成简体中文翻译。)

Source:

Webhook 概述

SendRec 已经支持 Slack 通知,但 Slack 只是其中一种渠道。
如果您需要在有人观看视频时触发 n8n 工作流、在观众点击号召性用语时向您的 CRM POST 数据,或将每个事件输送到自定义仪表盘,您就需要 通用 webhook —— 一个接收所有事件的 JSON POST 的单一 URL。

用户配置

  • 每位用户都可以在 设置 中配置一个 webhook URL。
  • 当他们的视频发生任何事件时,SendRec 会向该 URL POST 一个 JSON 负载。
{
  "event": "video.viewed",
  "timestamp": "2026-02-21T14:30:00Z",
  "data": {
    "videoId": "abc-123",
    "title": "Product Demo",
    "watchUrl": "https://app.sendrec.eu/watch/xyz",
    "viewCount": 5,
    "viewerHash": "sha256..."
  }
}

事件类型

七种事件类型覆盖了完整的视频生命周期:

事件描述
video.created创建了新的视频记录
video.ready视频已完成处理,准备好观看
video.deleted视频已被删除
video.viewed观众观看了视频
video.comment添加了评论
video.milestone达到了观看次数里程碑
video.cta_click点击了号召性用语按钮

负载签名

每个请求都会包含一个 X-Webhook-Signature 头部,以便接收方验证负载未被篡改。
我们使用 HMAC‑SHA256,并遵循 GitHub Webhook 相同的约定(sha256= 前缀)。

func SignPayload(secret string, payload []byte) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    return "sha256=" + hex.EncodeToString(mac.Sum(nil))
}
  • 当首次保存 webhook URL 时,密钥会自动生成——32 个随机字节,十六进制编码。
  • 接收方会重新计算请求体的 HMAC,并将其与头部值进行比较。
  • sha256= 前缀明确了使用的算法,并为将来可能的算法提供了空间,而不会破坏现有的集成。

重试与投递逻辑

Webhook 端点可能会宕机、部署重启或负载均衡器出现短暂故障。一次投递失败 不应 视为事件丢失。

重试策略

  • 最多 3 次尝试,采用指数退避(1s,随后 4s)。
  • 可配置的延迟 (c.retryDelays)。
  • 上下文取消会中止等待,从而防止关机时被阻塞。
func (c *Client) Dispatch(ctx context.Context, userID, webhookURL, secret string, event Event) error {
    body, err := json.Marshal(event)
    if err != nil {
        return fmt.Errorf("marshal webhook payload: %w", err)
    }

    signature := SignPayload(secret, body)
    maxAttempts := 1 + len(c.retryDelays)
    var lastErr error

    for attempt := 1; attempt = 200 && *statusCode  maxResponseBodyBytes {
        respBody = respBody[:maxResponseBodyBytes]
    }
}

我们读取比限制多一个字节以检测截断,然后裁剪到恰好 1024 字节。


调度助手(Fire‑and‑Forget)

该助手在后台 goroutine 中触发 webhook,复制现有的 Slack 通知模式。

func (h *Handler) dispatchWebhook(userID string, event webhook.Event) {
    if h.webhookClient == nil {
        return
    }
    go func() {
        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()
        webhookURL, secret, err := h.webhookClient.LookupConfigByUserID(ctx, userID)
        if err != nil {
            return
        }
        _ = h.webhookClient.Dispatch(ctx, userID, webhookURL, secret, event)
    }()
}
  • 对于已经在 goroutine 中运行的处理器(例如评论通知、里程碑记录、CTA 点击跟踪),我们直接调用 webhook 客户端——无需额外的 goroutine。

URL 验证与密钥生成

  • 仅限 HTTPS,但有一个例外:http://localhosthttp://127.0.0.1 允许用于本地开发。
  • URL 长度限制为 500 个字符
  • 空 URL 会清除 webhook(存储为 NULL)——无需单独的开关,遵循与 Slack 相同的模式。

当 webhook URL 首次保存且不存在密钥时,我们会自动生成一个:

func generateWebhookSecret() (string, error) {
    b := make([]byte, 32) // 32 random bytes
    if _, err := rand.Read(b); err != nil {
        return "", fmt.Errorf("generate secret: %w", err)
    }
    return hex.EncodeToString(b), nil
}

生成的密钥随后用于每次发送的 X-Webhook-Signature 头部。

TL;DR

  • 用户可配置的 webhook URL → 每个事件的 JSON 负载。
  • 使用 X-Webhook-SignatureHMAC‑SHA256 签名
  • 重试最多 3 次,采用指数退避,并进行完整日志记录。
  • 投递记录 存储在 webhook_deliveries 中(截断的响应体)。
  • 异步发送(fire‑and‑forget),永不阻塞面向用户的 HTTP 响应。
  • 强制使用 HTTPS(本地主机例外),并为每个用户 自动生成密钥

所有这些为 SendRec 提供了一个健壮、通用的 webhook 系统,能够与任何下游服务集成。

b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
    return "", err
}
return hex.EncodeToString(b), nil
}

更新 Webhook URL

当您更新 webhook URL 时,现有的 secret 会通过 COALESCE 保留下来:

INSERT INTO notification_preferences (user_id, webhook_url, webhook_secret)
VALUES ($1, $2, $3)
ON CONFLICT (user_id) DO UPDATE SET
    webhook_url = $2,
    webhook_secret = COALESCE(notification_preferences.webhook_secret, $3);

如果您需要 新的 secret,请使用专门的 “重新生成” 按钮。该按钮会通过与保存 URL 的端点不同的单独接口创建全新的 secret,这样您在每次更改端点时就不必手动复制新的 secret 了。


设置 → Webhook 部分

  • URL input – 带有 Save 按钮的字段。
  • Signing secret – 默认被遮蔽,提供 CopyRegenerate 按钮。
  • Send test event – 发送 webhook.test 事件,以便在没有真实视图的情况下验证投递。
  • Recent deliveries – 显示最近 50 次投递尝试,每条记录包含:
    • 事件类型
    • 状态徽章
    • 时间戳
    • 可展开的行,展示完整的负载和响应体
  • Events reference – 可折叠的表格,列出所有事件类型及其负载字段。

delivery log 是最有价值的部分:当你的 webhook 端点返回错误时,你可以准确看到发送的内容和返回的内容,而无需在服务器日志中翻找。

试用

SendRec 是开源的(AGPL‑3.0)且可自行托管。通用 webhook 已上线于 app.sendrec.eu

  1. 前往 SettingsWebhooks
  2. 添加一个 webhook URL。
  3. 点击 Send test event

Webhook 客户端实现可在 webhook.go 中找到。

0 浏览
Back to Blog

相关文章

阅读更多 »