การดูแลระบบ › Webhooks
เอกสาร/การดูแลระบบ/Webhooks

Webhooks

หน้านี้สอนอะไร

แจ้งบริการอื่นเมื่อเนื้อหาเปลี่ยน พร้อมลายเซ็นและการส่งซ้ำที่ไม่หายแม้ server รีสตาร์ต

ควรอ่านก่อน: Hooks

แจ้งบริการอื่นเมื่อเนื้อหาเปลี่ยนแปลง เช่น build static site ใหม่ ล้าง cache ของ CDN หรือแจ้งเตือนไปยัง ช่องแชต

export default defineConfig({
  webhooks: [
    // Every event of every collection and global, signed.
    { url: 'https://example.com/cms-hook', secret: process.env.WEBHOOK_SECRET },
    // Only publishing of posts, e.g. a Vercel or Netlify build hook.
    { url: process.env.BUILD_HOOK_URL!, events: ['publish', 'unpublish'], collections: ['posts'], globals: [] },
  ],
})
ตัวเลือกค่าเริ่มต้น
urlจำเป็นปลายทางที่ event จะถูก POST ไป
eventsทั้งหมดcreate, update, delete, publish, unpublish, draft
collectionsทั้งหมดslug ของ collection ใช้ [] เพื่อไม่เลือกเลย
globalsทั้งหมดslug ของ global ใช้ [] เพื่อไม่เลือกเลย
secret—ลงลายเซ็นทุก request (ดูด้านล่าง)
headers—header เพิ่มเติม เช่น token ที่ฝั่งรับใช้ตรวจสอบ

Events

Eventเมื่อใด
createมีการสร้างเอกสาร
updateเอกสารที่จัดเก็บไว้หรือ global มีการเปลี่ยนแปลง
deleteมีการลบเอกสาร
publishstatus เปลี่ยนเป็น published (ส่งพร้อมกับ create หรือ update)
unpublishstatus ไม่เป็น published อีกต่อไป
draftบันทึกเฉพาะฉบับร่าง เนื้อหาที่แสดงบนเว็บไม่เปลี่ยน (เวอร์ชัน)

การเผยแพร่ตามเวลาที่ตั้งไว้จะส่ง event เดียวกัน

Event ของคุณเอง

plugin และแอปส่ง event ของตัวเองได้ เช่น order.paid ของร้านค้า ประกาศชื่อไว้ใน events (plugin ประกาศให้เอง) ส่งด้วย cms.emit() แล้วระบุใน events ของ webhook event เหล่านี้ส่งไปเฉพาะ webhook ที่ระบุไว้

export default defineConfig({
  events: ['invoice.sent'],
  webhooks: [{ url: 'https://erp.example.com/hook', events: ['invoice.sent'], secret }],
})
 
cms.emit('invoice.sent', { number: 'INV-1' }, { collection: 'invoices', id: 7 })

body คือ { event, collection?, id?, doc, timestamp } โดย doc คือสิ่งที่ส่งให้ emit การส่ง ลายเซ็น และการส่งซ้ำเหมือนเดิม

Requests

POST /cms-hook
content-type: application/json
x-easy-cms-event: publish
x-easy-cms-delivery: 2f1c…            (the same on retries)
x-easy-cms-signature: sha256=9a0b…    (with `secret`)
 
{ "event": "publish", "collection": "posts", "id": 12, "doc": { … }, "timestamp": "…" }

doc คือเอกสารที่จัดเก็บไว้ ครบทุกภาษา โดยไม่มี field ที่ซ่อนอยู่ ตรวจสอบลายเซ็นด้วย HMAC-SHA256 ของ body ดิบ:

import { createHmac, timingSafeEqual } from 'node:crypto'
 
const expected = `sha256=${createHmac('sha256', secret).update(rawBody).digest('hex')}`
const valid =
  expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature))

Delivery

webhook จะถูกส่งหลังจากบันทึกการเปลี่ยนแปลงแล้ว และจะไม่ทำให้การบันทึกช้าลงหรือล้มเหลว กรณี timeout (10 วินาที), ข้อผิดพลาดของเครือข่าย, คำตอบ 429 และ 5xx จะลองส่งใหม่ ส่วนคำตอบ 4xx อื่นๆ จะถูกบันทึก log และไม่ลองใหม่

  • ลองใหม่เร็วๆ สองครั้งใน process ที่ทำการเปลี่ยนแปลง (หลัง 1 วินาทีและ 5 วินาที)
  • การส่งแต่ละครั้งจะถูกบันทึกลงฐานข้อมูล (ตารางภายใน webhook-deliveries) ก่อนส่งครั้งแรก และลบออกเมื่อสำเร็จ จึงไม่หายแม้ restart หรือฟังก์ชัน serverless หยุดทำงานกลางคันระหว่างส่ง ถ้า process หยุดระหว่างส่ง การรันรอบถัดไปจะส่งต่อให้หลังผ่านไป 5 นาที
  • หลังจากนั้นจะลองใหม่พร้อมกับงานที่ตั้งเวลาไว้: หลัง 1 นาที, 5 นาที, 30 นาที, 2, 6 และ 12 ชั่วโมง server จะรันทุกนาทีเอง ส่วน serverless ให้ cron เรียก <api>/jobs/run
  • หลังครั้งสุดท้าย (ประมาณหนึ่งวัน) การส่งจะถูกเก็บไว้เป็น state: 'failed' พร้อม error ล่าสุด และไม่ลองใหม่อีก admin ดูได้ที่ ตั้งค่า → การส่ง และกดส่งซ้ำหรือลบได้ (ตรวจสุขภาพระบบ) รายการที่ล้มจะถูกลบเมื่อเกิน 30 วัน

ทุกครั้งที่ลองใหม่จะส่ง body, ลายเซ็น และ x-easy-cms-delivery เหมือนเดิม ปลายทางจึงข้ามการส่งที่เคยจัดการแล้วได้ การเพิ่ม webhooks ใน config จะเพิ่มตาราง webhook-deliveries ให้สร้าง migration เหมือนการเปลี่ยน config ทั่วไป

บนแพลตฟอร์ม serverless ฟังก์ชันอาจหยุดทำงานก่อนที่ webhook จะถูกส่ง: ให้เรียก await cms.flushWebhooks() ก่อน return เมื่อเขียนข้อมูลผ่าน Local API บนแพลตฟอร์มเหล่านั้น ส่วน await cms.retryWebhooks() จะลองส่งรายการที่บันทึกไว้ซึ่งถึงเวลาแล้ว (runJobs() ทำทั้งส่วนนี้และการเผยแพร่ตามเวลา)

ขั้นต่อไป