Skip to content

Yazma, idempotency ve webhook'lar

This content is not available in your language yet.

  • Her yazma isteği Idempotency-Key başlığı ister (8–64 karakter, ör. UUID). Aynı anahtarla aynı istek 24 saat boyunca aynı yanıtı döner (Idempotent-Replayed: true); aynı anahtarla farklı gövde 422, hâlâ sürüyorsa 409.
  • İçerik, HADİ uygulamasındakiyle aynı moderasyondan geçer (küfür, cinsel içerik yasağı, yüz kapısı, EXIF/konum temizliği, taciz kalkanı, “yalnızca doğrulanmış” modu). İncelemeye düşen gönderi review: true döner.
  • Uygulamanın paylaştığı her gönderi ve yorumda ”🔌 Uygulama adı ile” etiketi görünür; kaldırılamaz.
  • Üye başına saatlik sınırlar tüm uygulamalar toplamıdır: yazma 60, takip 30, gönderi 20, yorum 30.
  • Fotoğraf: POST /v1/media/uploads ile 15 dakikalık tek kullanımlık adres al, JPEG’i PUT et, dönen mediaId’yi POST /v1/posts içinde mediaIds ile kullan. Ücretli etkinliklere API’den katılım şimdilik yok (403 ucretli-etkinlik).

Konsoldan uç nokta eklerken HADİ imzalı bir webhook.test gönderir; 2xx dönmelisin. Olaylar: grant.created, grant.revoked. Yük incedir — yalnızca tür, kimlik ve zaman; ayrıntıyı API’den (yetki yeniden denetlenerek) çek.

Başlıklar (Standard Webhooks): webhook-id, webhook-timestamp, webhook-signature: es256,<base64 imza>, webhook-key-id. İmza ES256 (ECDSA P-256 + SHA-256, 64 bayt r ∥ s); anahtar HADİ’nin donanım güvenlik modülünde (Cloud KMS HSM). İmzalanan metin: `${id}.${timestamp}.${gövde}`. Açık anahtar: https://auth.hadisosyal.com/jwks.json içinde use: "webhook" olan EC anahtar (kid = webhook-key-id).

Etiket neden es256? Standard Webhooks’ta v1 HMAC’e, v1a Ed25519’a ayrılmıştır. HADİ ES256 kullandığı için kendi etiketini yazar. Anahtar döndürülürken başlıkta boşlukla ayrılmış birden çok imza olabilir; biri geçerliyse kabul et.

import { createPublicKey, verify } from "node:crypto";
export async function verifyHadiWebhook(headers: Headers, rawBody: string): Promise<boolean> {
const id = headers.get("webhook-id"), ts = headers.get("webhook-timestamp"), sig = headers.get("webhook-signature");
if (!id || !ts || !sig) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // 5 dk tolerans
const { keys } = await (await fetch("https://auth.hadisosyal.com/jwks.json")).json(); // önbelleğe al
const jwk = keys.find((k: { use: string; kid: string }) => k.use === "webhook" && k.kid === headers.get("webhook-key-id"));
if (!jwk) return false;
const key = createPublicKey({ key: { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y }, format: "jwk" });
const data = Buffer.from(`${id}.${ts}.${rawBody}`);
return sig.split(" ").some((p) => p.startsWith("es256,")
&& verify("sha256", data, { key, dsaEncoding: "ieee-p1363" }, Buffer.from(p.slice(6), "base64")));
}

webhook-id’yi kaydet ve aynı kimliği ikinci kez işleme (tekrar koruması). Teslim “en az bir kez”dir; başarısız teslimler 24 saate kadar üstel aralıklarla yeniden denenir, 3 gün kesintisiz hata uç noktayı devre dışı bırakır, 410 yanıtı hemen devre dışı bırakır. Uç nokta herkese açık bir alan adında, https ve 443 portunda olmalı.