diff --git a/docs/design/13-toolchain-and-dev-workflow.md b/docs/design/13-toolchain-and-dev-workflow.md index 7b90345..6e014d7 100644 --- a/docs/design/13-toolchain-and-dev-workflow.md +++ b/docs/design/13-toolchain-and-dev-workflow.md @@ -1697,16 +1697,19 @@ templates/toolchain/ ```python # src/api/toolchain_routes.py -from fastapi import APIRouter, Request, Header, HTTPException +import asyncio +from fastapi import APIRouter, Request, Response +from src.config.agents import AGENT_IDS from src.daemon.toolchain_templates import TemplateEngine router = APIRouter() engine = TemplateEngine() GITEA_WEBHOOK_SECRET = os.environ.get("GITEA_WEBHOOK_SECRET", "") +_idempotency_lock = asyncio.Lock() @router.post("/webhook/gitea") -async def handle_gitea_webhook( +async def gitea_webhook( request: Request, x_gitea_event: str = Header(...), x_gitea_signature: str = Header(None), @@ -1716,32 +1719,32 @@ async def handle_gitea_webhook( body = await request.body() - # 1. 签名验证(可选,假设 Gitea 使用 HMAC-SHA256,需根据 Gitea 版本确认) + # 1. 签名验证(HMAC-SHA256,Gitea 1.23.4 已确认) if GITEA_WEBHOOK_SECRET: - expected = hmac.new(GITEA_WEBHOOK_SECRET.encode(), body, sha256).hexdigest() + expected = hmac.new(GITEA_WEBHOOK_SECRET.encode(), body, hashlib.sha256).hexdigest() if not hmac.compare_digest(expected, (x_gitea_signature or "")): - raise HTTPException(403, "Invalid signature") + return Response(status_code=403, content="signature verification failed") event = json.loads(body) - # 2. 幂等检查(delivery ID = X-Gitea-Delivery header,Gitea 全局唯一) - event_key = f"{x_gitea_event}-{x_gitea_delivery}" - if is_duplicate(event_key): - return {"status": "duplicate"} + # 2. 幂等检查(asyncio.Lock 防并发竞态) + if x_gitea_event and x_gitea_delivery: + async with _idempotency_lock: + if _is_duplicate(x_gitea_event, x_gitea_delivery): + return Response(status_code=200, content="duplicate") # 3. 路由到对应处理器 handler = HANDLERS.get(x_gitea_event) if not handler: - logger.info("Ignoring unhandled event: %s", x_gitea_event) - return {"status": "ignored"} + return Response(status_code=200, content="ignored") # 4. 处理事件 → 创建 Mail try: result = await handler(engine, event) - return {"status": "ok", "mail_id": result} + return Response(status_code=200) except Exception as e: logger.exception("Failed to handle %s event", x_gitea_event) - raise HTTPException(500, str(e)) + return Response(status_code=500, content=str(e)) ``` ### 4.3 事件处理器