From 2b00269d7db9ced31c1b7a809ffa15932439ce81 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 9 Jun 2026 07:46:02 +0800 Subject: [PATCH] auto-sync: 2026-06-09 07:46:02 --- src/api/toolchain_routes.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index c0f3abe..504e884 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -46,17 +46,37 @@ _TTL_SECONDS = 7 * 24 * 3600 _idempotency_lock = asyncio.Lock() -def _is_duplicate(event: str, delivery: str) -> bool: - """检查 Webhook 是否重复投递,自动清理过期条目。""" +def _is_duplicate(event: str, delivery: str, payload: Optional[Dict[str, Any]] = None) -> bool: + """检查 Webhook 是否重复投递,自动清理过期条目。 + + 双重去重策略: + 1. delivery UUID 去重(标准幂等) + 2. payload 内容去重(应对 Gitea v1.23.4 的 webhookNotifier + actionsNotifier + 对同一 review 生成不同 UUID 的双投递问题) + """ now = time.time() # 清理过期条目 while _delivery_timestamps and (now - _delivery_timestamps[0][0]) > _TTL_SECONDS: _, key = _delivery_timestamps.pop(0) _delivery_cache.discard(key) + # 检查 delivery UUID 去重 key = f"{event}-{delivery}" if key in _delivery_cache: return True + + # 检查 payload 内容去重(review 事件:同一 PR + 同一用户 + 同一内容) + if payload and "review" in event: + pr_num = payload.get("pull_request", {}).get("number") + sender = payload.get("sender", {}).get("login") + content = payload.get("review", {}).get("content", "") + content_key = f"content:{event}:{pr_num}:{sender}:{content}" + if content_key in _delivery_cache: + logger.info("Content-based duplicate detected: %s PR#%s by %s", event, pr_num, sender) + return True + _delivery_cache.add(content_key) + _delivery_timestamps.append((now, content_key)) + _delivery_cache.add(key) _delivery_timestamps.append((now, key)) return False @@ -462,14 +482,7 @@ async def gitea_webhook( logger.warning("Webhook signature verification failed") return Response(status_code=403, content="signature verification failed") - # 2. 幂等检查 - if x_gitea_event and x_gitea_delivery: - async with _idempotency_lock: - if _is_duplicate(x_gitea_event, x_gitea_delivery): - logger.debug("Duplicate webhook: %s/%s", x_gitea_event, x_gitea_delivery) - return Response(status_code=200, content="duplicate") - - # 3. 解析 payload + # 3. 解析 payload(提前解析,用于幂等检查) try: payload = await request.json() except Exception: