/** * slides/15-webhook-endpoints.js * ---------------------------------------------------------------------------- * Slide 15 -- Webhook endpoints: FastAPI handlers * /webhooks/{github,linear,slack} */ 'use strict'; const { helpers, layouts } = require('../../design-system'); function buildWebhookEndpoints(pres, theme) { const slide = pres.addSlide(); helpers.slideBase(slide, pres, theme); helpers.addHeader(slide, pres, theme, { eyebrow: 'STAGE 4', sectionNumber: 4, title: 'Webhook endpoints', }); helpers.addCodeBlock(slide, pres, theme, { x: 0.5, y: layouts.CONTENT_TOP, w: 6.0, h: 3.5, language: 'python', code: [ '# apps/open-swe/webhooks.py', 'from fastapi import APIRouter, Request', 'from open_swe.triggers.router import (', ' thread_id_for,', ')', '', 'router = APIRouter()', '', '@router.post("/webhooks/slack")', 'async def slack_webhook(req: Request):', ' payload = await req.json()', ' if "@open-swe" not in payload["text"]:', ' return {"ok": True}', ' await enqueue_run(thread_id_for(', ' "slack", payload["channel"]))', ].join('\n'), filePath: 'apps/open-swe/webhooks.py', startLine: 1, highlightLines: [9, 10, 11, 12, 13, 14, 15], }); helpers.addCallout(slide, pres, theme, { x: 6.7, y: layouts.CONTENT_TOP, w: 2.8, h: 1.55, kind: 'success', title: 'Idempotency', text: 'Webhook вычисляет thread_id ' + 'и проверяет наличие run. ' + 'Если есть -- enqueue, ' + 'иначе новый run.', }); helpers.addCallout(slide, pres, theme, { x: 6.7, y: 2.95, w: 2.8, h: 1.9, kind: 'warning', title: 'Signature', text: 'Все handlers обязаны ' + 'проверять X-Signature от ' + 'Slack / Linear / GitHub. ' + 'Не доверяйте без verify.', }); helpers.addPageNumber(slide, pres, theme, 15); helpers.addSourceLine(slide, pres, theme, { source: 'research/per-tech/openswe.md + FastAPI webhook patterns', }); return slide; } module.exports = { buildWebhookEndpoints };