跳至正文
mini.casa
开发者

为耗时工作提供可靠信号

当任务、域名、发布或批准发生变化时,发送带签名的 HTTPS 请求,并保证至少投递一次。

注册端点

在开发者页面添加端点,或使用 webhooks:write 权限范围:

curl -s -X POST "$BASE/workspaces/$WS/webhooks" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $(openssl rand -hex 16)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/mini-casa","events":["job.updated","publication.updated"]}'

响应包含 signing_secret(whsec_...),它只会显示一次。URL 必须通过 443 端口使用 HTTPS,且只能解析到公共地址;私有地址、回环地址和内部目标会被拒绝。每次投递前都会重新检查,且绝不会跟随重定向。一个工作区最多可有 10 个启用中的端点。

证明你拥有控制权

新端点最初处于 pending_verification 状态。验证按钮(或 POST /webhooks/{id}/verify)会发送一次带签名的 webhook.challenge。返回 2xx 状态码和 {"challenge": "<data.challenge>"},端点就会变为 active。

事件

  • job.updated:任务或批量运行的状态发生变化。
  • job.item.updated:批量运行中某个项目的状态发生变化。
  • domain.updated:自定义域名进入不同检查阶段。
  • publication.updated:构建开始、成功、失败或被后续构建取代。
  • approval.updated:批准请求得到决定、到期或被使用。
  • billing.entitlement.updated:套餐或权益发生变化。

请求体只携带 ID 和状态,绝不包含凭据、内容或个人数据。请通过 API 获取当前状态。

验证每一个请求

每个请求都包含 SmallWorld-Webhook-Id、SmallWorld-Webhook-Timestamp(Unix 秒数)和 SmallWorld-Webhook-Signature(v1=<hex>,kid=<key id>)。签名是以你的密钥对 <timestamp>.<raw body> 计算的 HMAC-SHA256。请在进行任何 JSON 解析之前使用原始请求体,以恒定时间比较签名,并拒绝时间戳与你的时钟相差超过五分钟的请求。

import crypto from "node:crypto";

export function verify(secret, timestamp, rawBody, header) {
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const given = /v1=([a-f0-9]+)/.exec(header)?.[1] ?? "";
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
  return fresh && given.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

投递保证

  • 至少一次。请求会持续重试,直到你返回 2xx。每次重试和重新投递时,事件 ID 都保持不变,因此请保存它,并忽略已经处理过的 ID。
  • 重试间隔会从一分钟逐步延长到一天,总计持续 72 小时。此后,投递会成为死信;你可以在开发者页面或使用 redeliver_webhook 再次发送。
  • 一个端点连续有 10 个事件成为死信后,会被暂停,直到你恢复它。
  • 不保证投递顺序:请将状态视为提示,并读取资源以确认。