From e39f32dbf43760b2eaa759d342f8da230a8fab40 Mon Sep 17 00:00:00 2001 From: zhang_heng <3349123936@qq.com> Date: Wed, 16 Jul 2025 02:57:20 +0800 Subject: [PATCH] 11 --- src/api/Webhook.py | 915 ++++++++++++++------------------------------- 1 file changed, 286 insertions(+), 629 deletions(-) diff --git a/src/api/Webhook.py b/src/api/Webhook.py index 02cbde407..b25771b98 100644 --- a/src/api/Webhook.py +++ b/src/api/Webhook.py @@ -136,30 +136,48 @@ async def get_project_id_from_webhook(data: dict) -> int: """ repo_url = None - # 从不同平台的webhook数据中提取仓库URL - if "repository" in data: - # GitHub/Gitee格式 - if "html_url" in data["repository"]: - repo_url = data["repository"]["html_url"] - elif "url" in data["repository"]: - repo_url = data["repository"]["url"] - elif "clone_url" in data["repository"]: - repo_url = data["repository"]["clone_url"] - elif "project" in data: - # GitLink格式 - if "html_url" in data["project"]: - repo_url = data["project"]["html_url"] - elif "git_url" in data["project"]: - repo_url = data["project"]["git_url"] + try: + # 从不同平台的webhook数据中提取仓库URL + if "repository" in data: + # GitHub/Gitee格式 + if "html_url" in data["repository"]: + repo_url = data["repository"]["html_url"] + elif "url" in data["repository"]: + repo_url = data["repository"]["url"] + elif "clone_url" in data["repository"]: + repo_url = data["repository"]["clone_url"] + elif "project" in data: + # GitLink格式 + project = data["project"] + if "html_url" in project: + repo_url = project["html_url"] + elif "git_url" in project: + repo_url = project["git_url"] + else: + # 如果没有直接的URL,尝试构建URL + project_name = project.get("name") + project_owner = project.get("owner", {}).get("login") or project.get("owner", {}).get("name") + if project_name and project_owner: + repo_url = f"https://www.gitlink.org.cn/{project_owner}/{project_name}" + logger.info(f"🔍 从项目信息构建GitLink URL: {repo_url}") - if repo_url: - project_id = await find_project_by_repo_url(repo_url) - if project_id: - return project_id + if repo_url: + logger.info(f"🔍 尝试通过仓库URL匹配项目: {repo_url}") + project_id = await find_project_by_repo_url(repo_url) + if project_id: + logger.info(f"✅ 成功匹配到项目ID: {project_id}") + return project_id + else: + logger.warning(f"⚠️ 未找到匹配的项目: {repo_url}") + else: + logger.warning("⚠️ 无法从webhook数据中提取仓库URL") - # 如果找不到匹配的项目,使用默认项目ID=1 - logger.warning(f"🔄 使用默认项目ID=1,仓库URL: {repo_url}") - return 1 + # 如果找不到匹配的项目,使用默认项目ID=1 + logger.warning(f"🔄 使用默认项目ID=1,这可能会导致同步失败") + return 1 + except Exception as e: + logger.error(f"❌ 项目ID提取失败: {str(e)}") + return 1 async def get_sync_repo(project_id: int = 1): """获取同步仓库配置""" @@ -176,7 +194,7 @@ async def get_sync_repo(project_id: int = 1): config.get("github", {}).get("owner") ) -async def verify_webhook_signature(request: Request, body: bytes, project_id: int = 1) -> None: +async def verify_webhook_signature(request: Request, body: bytes, source: str, project_id: int = 1) -> None: """验证webhook签名""" config = await get_config(project_id) @@ -185,22 +203,22 @@ async def verify_webhook_signature(request: Request, body: bytes, project_id: in github_signature = request.headers.get("X-Hub-Signature-256") gitlink_signature = request.headers.get("X-Gitlink-Signature") - # 根据签名类型验证 - if gitee_signature: + # 根据来源平台验证签名 + if source == "Gitee": gitee_webhook_secret = config.get("gitee", {}).get("webhook_secret") if not gitee_webhook_secret: logger.info("🔓 Gitee webhook签名验证跳过(未配置secret)") - return # 如果未配置secret,跳过验证 + return if gitee_signature != gitee_webhook_secret: logger.error("❌ Gitee webhook签名验证失败") raise HTTPException(status_code=401, detail="Invalid Gitee webhook signature") logger.info("✅ Gitee webhook签名验证成功") - elif github_signature: + elif source == "GitHub": github_webhook_secret = config.get("github", {}).get("webhook_secret") if not github_webhook_secret: logger.info("🔓 GitHub webhook签名验证跳过(未配置secret)") - return # 如果未配置secret,跳过验证 + return # GitHub使用HMAC-SHA256签名验证 expected_signature = hmac.new( @@ -215,476 +233,265 @@ async def verify_webhook_signature(request: Request, body: bytes, project_id: in raise HTTPException(status_code=401, detail="Invalid GitHub webhook signature") logger.info("✅ GitHub webhook签名验证成功") - elif gitlink_signature: + elif source == "GitLink": gitlink_webhook_secret = config.get("gitlink", {}).get("webhook_secret") if not gitlink_webhook_secret: logger.info("🔓 GitLink webhook签名验证跳过(未配置secret)") - return # 如果未配置secret,跳过验证 + return if gitlink_signature != gitlink_webhook_secret: logger.error("❌ GitLink webhook签名验证失败") raise HTTPException(status_code=401, detail="Invalid GitLink webhook signature") logger.info("✅ GitLink webhook签名验证成功") else: - logger.warning("⚠️ 未找到webhook签名头") - raise HTTPException(status_code=401, detail="Missing webhook signature") + logger.warning(f"⚠️ 未知的webhook来源: {source}") + raise HTTPException(status_code=401, detail="Unknown webhook source") + +async def sync_issue_to_gitee(data: dict, token: str, owner: str, repo: str) -> dict: + """同步issue到Gitee""" + try: + issue = data["issue"] + title = issue["subject"] + body = issue.get("description", "") + + # 在Gitee上创建issue + url = f'https://gitee.com/api/v5/repos/{owner}/issues' + payload = { + "access_token": token, + "repo": repo, + "title": title, + "body": body + } + headers = { + 'Content-Type': 'application/json;charset=UTF-8' + } + + logger.info(f"📤 向Gitee发送请求: POST {url}") + response = requests.post(url, headers=headers, json=payload) + response.raise_for_status() + + result = response.json() + return { + "issue_number": result.get("number"), + "url": result.get("html_url") + } + except Exception as e: + logger.error(f"❌ Gitee同步失败: {str(e)}") + raise + +async def sync_issue_to_github(data: dict, token: str, owner: str, repo: str) -> dict: + """同步issue到GitHub""" + try: + issue = data["issue"] + title = issue["subject"] + body = issue.get("description", "") + + # 在GitHub上创建issue + url = f"https://api.github.com/repos/{owner}/{repo}/issues" + payload = { + "title": title, + "body": body, + "labels": ["sync issue"] + } + headers = { + 'Authorization': f'Bearer {token}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + + logger.info(f"📤 向GitHub发送请求: POST {url}") + response = requests.post(url, headers=headers, json=payload) + response.raise_for_status() + + result = response.json() + return { + "issue_number": result.get("number"), + "url": result.get("html_url") + } + except Exception as e: + logger.error(f"❌ GitHub同步失败: {str(e)}") + raise + +async def sync_issue_to_gitlink(data: dict, token: str, owner: str, repo: str) -> dict: + """同步issue到GitLink""" + try: + # 从Gitee的webhook数据中提取issue信息 + issue = data.get("issue", {}) + title = issue.get("title", "") + body = issue.get("body", "") + + # 构造GitLink API请求 + url = f"https://www.gitlink.org.cn/api/v1/repos/{owner}/{repo}/issues" + headers = { + 'Authorization': f'Bearer {token}', + 'Content-Type': 'application/json' + } + payload = { + "subject": title, + "description": body, + "labels": ["sync issue"] + } + + logger.info(f"📤 向GitLink发送请求: POST {url}") + response = requests.post(url, headers=headers, json=payload) + response.raise_for_status() + + result = response.json() + return { + "issue_number": result.get("number"), + "url": result.get("html_url") + } + except Exception as e: + logger.error(f"❌ GitLink同步失败: {str(e)}") + raise @WEBHOOK.post("/issue") async def issue_webhook(request: Request): """处理issue相关的webhook事件 - 自动识别项目""" start_time = datetime.now() - logger.info("🚀 =========================") - logger.info("🚀 收到Issue Webhook请求") - logger.info("🚀 =========================") - - # 先读取请求体 - body = await request.body() - - # 解析JSON数据以获取项目信息 - data = json.loads(body.decode('utf-8')) - - # 检测webhook来源和事件信息 - source = detect_webhook_source(data, dict(request.headers)) - event_info = get_webhook_event_info(data, source) - - logger.info(f"📡 来源平台: {event_info['source']}") - logger.info(f"📝 事件类型: {event_info['event_type']}") - logger.info(f"🎯 动作: {event_info['action']}") - logger.info(f"📁 仓库: {event_info['repository']}") - logger.info(f"👤 用户: {event_info['user']}") - logger.info(f"🏷️ 标题: {event_info['title']}") - logger.info(f"🔢 编号: #{event_info['number']}") - - # 自动查找对应的项目ID - project_id = await get_project_id_from_webhook(data) - logger.info(f"🆔 使用项目ID: {project_id}") - - # 验证签名 - await verify_webhook_signature(request, body, project_id) - - # 获取仓库配置 - row = await get_sync_repo(project_id) - gitlink_token = row[0] - gitlink_repo = row[1] - gitlink_owner = row[2] - gitee_token = row[3] - gitee_repo = row[4] - gitee_owner = row[5] - github_token = row[6] - github_repo = row[7] - github_owner = row[8] - - # 获取webhook数据 - # data = await request.json() # This line is removed as per the edit hint - - # 检测来源平台 - if "repository" in data: - if data["repository"]["owner"]["avatar_url"] and "gitee" in data["repository"]["owner"]["avatar_url"]: - flag = "gitee" - else: - flag = "github" - else: - flag = "gitlink" - - # 处理来自不同平台的issue事件 - if flag == "gitee": - logger.info("🔄 开始处理Gitee Issue事件") - # 处理Gitee的issue事件 - if data["issue"] and data["action"] == "open": - # 创建issue - title = data["issue"]["title"] - body = data["issue"]["body"] - gitee_issue_index = data["issue"]["number"] + try: + logger.info("🚀 =========================") + logger.info("🚀 收到Issue Webhook请求") + logger.info("🚀 =========================") + + # 先读取请求体 + body = await request.body() + + # 解析JSON数据以获取项目信息 + try: + data = json.loads(body.decode('utf-8')) + except json.JSONDecodeError as e: + logger.error(f"❌ JSON解析失败: {str(e)}") + return {"status": "error", "message": "Invalid JSON data"} + + # 检测webhook来源和事件信息 + source = detect_webhook_source(data, dict(request.headers)) + event_info = get_webhook_event_info(data, source) + + # 记录事件信息 + logger.info(f"📡 来源平台: {event_info['source']}") + logger.info(f"📝 事件类型: {event_info['event_type']}") + logger.info(f"🎯 动作: {event_info['action']}") + logger.info(f"📁 仓库: {event_info['repository']}") + logger.info(f"👤 用户: {event_info['user']}") + logger.info(f"🏷️ 标题: {event_info['title']}") + logger.info(f"🔢 编号: #{event_info['number']}") + + # 自动查找对应的项目ID + project_id = await get_project_id_from_webhook(data) + logger.info(f"🆔 使用项目ID: {project_id}") + + if project_id == 1: + logger.warning("⚠️ 使用默认项目ID,请检查项目配置") + + # 验证签名 + await verify_webhook_signature(request, body, event_info["source"], project_id) + + # 获取仓库配置 + row = await get_sync_repo(project_id) + gitlink_token, gitlink_repo, gitlink_owner = row[0:3] + gitee_token, gitee_repo, gitee_owner = row[3:6] + github_token, github_repo, github_owner = row[6:9] + + # 验证配置完整性 + if event_info["source"] == "GitLink": + if not all([gitlink_token, gitlink_repo, gitlink_owner]): + logger.error("❌ GitLink配置不完整") + return {"status": "error", "message": "GitLink configuration incomplete"} + elif event_info["source"] == "Gitee": + if not all([gitee_token, gitee_repo, gitee_owner]): + logger.error("❌ Gitee配置不完整") + return {"status": "error", "message": "Gitee configuration incomplete"} + elif event_info["source"] == "GitHub": + if not all([github_token, github_repo, github_owner]): + logger.error("❌ GitHub配置不完整") + return {"status": "error", "message": "GitHub configuration incomplete"} + + # 处理issue事件 + if event_info["event_type"] != "issue": + logger.warning(f"⚠️ 非issue事件: {event_info['event_type']}") + return {"status": "ignored", "message": "Not an issue event"} - logger.info(f"📝 创建Issue: #{gitee_issue_index} - {title}") + if event_info["action"] not in ["opened", "edited", "closed", "reopened"]: + logger.info(f"ℹ️ 忽略的issue动作: {event_info['action']}") + return {"status": "ignored", "message": f"Ignored issue action: {event_info['action']}"} + + # 同步处理 + sync_results = [] + + # 从GitLink同步到其他平台 + if event_info["source"] == "GitLink" and event_info["action"] == "opened": + logger.info("🔄 开始处理GitLink Issue事件") + logger.info("📝 创建Issue: #{} - {}", event_info["number"], event_info["title"]) + logger.info("🔄 开始同步到Gitee和GitHub...") + + # 同步到Gitee + if all([gitee_token, gitee_repo, gitee_owner]): + try: + logger.info("📤 正在向Gitee创建Issue...") + gitee_result = await sync_issue_to_gitee(data, gitee_token, gitee_owner, gitee_repo) + sync_results.append({"platform": "Gitee", "status": "success", "result": gitee_result}) + logger.info("✅ Gitee Issue创建成功") + except Exception as e: + logger.error(f"❌ Gitee同步失败: {str(e)}") + sync_results.append({"platform": "Gitee", "status": "error", "error": str(e)}) + + # 同步到GitHub + if all([github_token, github_repo, github_owner]): + try: + logger.info("📤 正在向GitHub创建Issue...") + github_result = await sync_issue_to_github(data, github_token, github_owner, github_repo) + sync_results.append({"platform": "GitHub", "status": "success", "result": github_result}) + logger.info("✅ GitHub Issue创建成功") + except Exception as e: + logger.error(f"❌ GitHub同步失败: {str(e)}") + sync_results.append({"platform": "GitHub", "status": "error", "error": str(e)}) + + # 从Gitee同步到其他平台 + elif event_info["source"] == "Gitee" and event_info["action"] in ["open", "opened"]: + logger.info("🔄 开始处理Gitee Issue事件") + logger.info("📝 创建Issue: #{} - {}", event_info["number"], event_info["title"]) logger.info("🔄 开始同步到GitLink和GitHub...") - - # 在GitLink上创建issue - gitlink_url = f"https://www.gitlink.org.cn/api/v1/{gitlink_owner}/{gitlink_repo}/issues.json" - current_time = datetime.now() - formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S') - gitlink_payload = { - "status_id": 1, - "priority_id": 2, - "start_date": formatted_time, - "subject": title, - "description": body - } - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - # 在GitHub上创建issue - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues" - github_payload = { - "body": body, - "labels": ["sync issue"], - "title": title - } - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - logger.info("📤 正在向GitLink创建Issue...") - gitlink_response = requests.post(gitlink_url, headers=gitlink_headers, json=gitlink_payload) - gitlink_response.raise_for_status() - logger.info("✅ GitLink Issue创建成功") - - logger.info("📤 正在向GitHub创建Issue...") - github_response = requests.post(github_url, headers=github_headers, json=github_payload) - github_response.raise_for_status() - logger.info("✅ GitHub Issue创建成功") - - # 记录处理时间 - end_time = datetime.now() - process_time = (end_time - start_time).total_seconds() - logger.info("🎉 =========================") - logger.info(f"🎉 Issue同步完成! 耗时: {process_time:.2f}秒") - logger.info("🎉 =========================") - - return {"message": "issue创建同步成功!", "processed_in": f"{process_time:.2f}s"} - except requests.RequestException as e: - logger.error(f"❌ Issue同步失败: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) - - elif data["issue"]["state"] == "rejected" and data["action"] == "state_change": - # 处理issue关闭事件 - issue_title = data["issue"]["title"] - gitee_issue_index = data["issue"]["number"] - logger.info(f"🚫 关闭Issue: #{gitee_issue_index} - {issue_title}") - logger.info("🔄 开始同步关闭状态到GitLink和GitHub...") + # 同步到GitLink + if all([gitlink_token, gitlink_repo, gitlink_owner]): + try: + logger.info("📤 正在向GitLink创建Issue...") + gitlink_result = await sync_issue_to_gitlink(data, gitlink_token, gitlink_owner, gitlink_repo) + sync_results.append({"platform": "GitLink", "status": "success", "result": gitlink_result}) + logger.info("✅ GitLink Issue创建成功") + except Exception as e: + logger.error(f"❌ GitLink同步失败: {str(e)}") + sync_results.append({"platform": "GitLink", "status": "error", "error": str(e)}) - # 在GitLink上关闭issue - gitlink_url = f"https://www.gitlink.org.cn/api/v1/{gitlink_owner}/{gitlink_repo}/issues/{gitee_issue_index}.json" - gitlink_payload = {"status_id": 5} - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - # 在GitHub上关闭issue - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues/{gitee_issue_index}" - github_payload = {"state": "closed"} - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - logger.info("📤 正在在GitLink关闭Issue...") - gitlink_response = requests.patch(gitlink_url, headers=gitlink_headers, json=gitlink_payload) - gitlink_response.raise_for_status() - logger.info("✅ GitLink Issue关闭成功") - - logger.info("📤 正在在GitHub关闭Issue...") - github_response = requests.patch(github_url, headers=github_headers, json=github_payload) - github_response.raise_for_status() - logger.info("✅ GitHub Issue关闭成功") - - end_time = datetime.now() - process_time = (end_time - start_time).total_seconds() - logger.info("🎉 =========================") - logger.info(f"🎉 Issue关闭同步完成! 耗时: {process_time:.2f}秒") - logger.info("🎉 =========================") - - return {"message": "issue关闭同步成功", "processed_in": f"{process_time:.2f}s"} - except requests.RequestException as e: - logger.error(f"❌ Issue关闭同步失败: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) - - elif data["action"] == "comment" and data["issue"]: - # 处理issue评论事件 - issue_title = data["issue"]["title"] - body = data["comment"]["body"] - gitee_issue_index = data["issue"]["number"] - - logger.info(f"💬 新评论: Issue #{gitee_issue_index}") - logger.info(f"💬 评论内容: {body[:50]}...") - logger.info("🔄 开始同步评论到GitLink和GitHub...") - - # 在GitLink上创建issue评论 - gitlink_url = f"https://www.gitlink.org.cn/api/v1/{gitlink_owner}/{gitlink_repo}/issues/{gitee_issue_index}/journals.json" - gitlink_payload = { - "notes": body, - "receivers_login": ["string"] - } - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - # 在GitHub上创建issue评论 - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues/{gitee_issue_index}/comments" - github_payload = {"body": body} - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - logger.info("📤 正在向GitLink添加评论...") - gitlink_response = requests.post(gitlink_url, headers=gitlink_headers, json=gitlink_payload) - gitlink_response.raise_for_status() - logger.info("✅ GitLink评论添加成功") - - logger.info("📤 正在向GitHub添加评论...") - github_response = requests.post(github_url, headers=github_headers, json=github_payload) - github_response.raise_for_status() - logger.info("✅ GitHub评论添加成功") - - end_time = datetime.now() - process_time = (end_time - start_time).total_seconds() - logger.info("🎉 =========================") - logger.info(f"🎉 评论同步完成! 耗时: {process_time:.2f}秒") - logger.info("🎉 =========================") - - return {"message": "issue评论同步成功", "processed_in": f"{process_time:.2f}s"} - except requests.RequestException as e: - logger.error(f"❌ 评论同步失败: {str(e)}") - raise HTTPException(status_code=500, detail=str(e)) - - elif flag == "gitlink": - # 处理GitLink的issue事件 - if data["action"] == "opened" and data["issue"]: - # GitLink创建issue - issue_title = data["issue"]["subject"] - body = data["issue"]["description"] - gitlink_issue_index = data["number"] - - # 在Gitee上创建issue - gitee_url = f'https://gitee.com/api/v5/repos/{gitee_owner}/issues' - gitee_payload = { - "access_token": gitee_token, - "repo": gitee_repo, - "title": issue_title, - "body": body - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - # 在GitHub上创建issue - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues" - github_payload = { - "body": body, - "labels": ["sync issue"], - "title": issue_title - } - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - github_response = requests.post(github_url, headers=github_headers, json=github_payload) - gitee_response = requests.post(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - github_response.raise_for_status() - return {"message": "同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) - - elif data["action"] == "created" and data["issue"]: - # GitLink创建issue评论 - issue_title = data["issue"]["subject"] - body = data["journal"]["notes"] - gitlink_issue_index = data["number"] - - # 在Gitee上创建评论 - gitee_url = f"https://gitee.com/api/v5/repos/{gitee_owner}/{gitee_repo}/issues/{gitlink_issue_index}/comments" - gitee_payload = { - "access_token": gitee_token, - "body": body - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - # 在GitHub上创建评论 - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues/{gitlink_issue_index}/comments" - github_payload = {"body": body} - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - gitee_response = requests.post(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - github_response = requests.post(github_url, headers=github_headers, json=github_payload) - github_response.raise_for_status() - return {"message": "issue评论同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) - - elif data["action"] == "edited" and data["issue"]["status"]["name"] == "关闭": - # GitLink关闭issue - issue_title = data["issue"]["subject"] - gitlink_issue_index = data["number"] - - # 在Gitee上关闭issue - gitee_url = f"https://gitee.com/api/v5/repos/{gitee_owner}/issues/{gitlink_issue_index}" - gitee_payload = { - "access_token": gitee_token, - "repo": gitee_repo, - "state": "closed" - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - # 在GitHub上关闭issue - github_url = f"https://api.github.com/repos/{github_owner}/{github_repo}/issues/{gitlink_issue_index}" - github_payload = {"state": "closed"} - github_headers = { - 'Authorization': f'Bearer {github_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - gitee_response = requests.patch(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - github_response = requests.patch(github_url, headers=github_headers, json=github_payload) - github_response.raise_for_status() - return {"message": "issue关闭同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) + # 同步到GitHub(只有在GitHub配置存在时才同步) + if all([github_token, github_repo, github_owner]): + try: + logger.info("📤 正在向GitHub创建Issue...") + github_result = await sync_issue_to_github(data, github_token, github_owner, github_repo) + sync_results.append({"platform": "GitHub", "status": "success", "result": github_result}) + logger.info("✅ GitHub Issue创建成功") + except Exception as e: + logger.error(f"❌ GitHub同步失败: {str(e)}") + sync_results.append({"platform": "GitHub", "status": "error", "error": str(e)}) - elif flag == "github": - # 处理GitHub的issue事件 - if data["action"] == "opened" and data["issue"]: - # GitHub创建issue - issue_title = data["issue"]["title"] - body = data["issue"]["body"] - github_issue_index = data["issue"]["number"] - - # 在Gitee上创建issue - gitee_url = f'https://gitee.com/api/v5/repos/{gitee_owner}/issues' - gitee_payload = { - "access_token": gitee_token, - "repo": gitee_repo, - "title": issue_title, - "body": body - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - # 在GitLink上创建issue - gitlink_url = f"https://www.gitlink.org.cn/api/v1/{gitlink_owner}/{gitlink_repo}/issues.json" - current_time = datetime.now() - formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S') - gitlink_payload = { - "status_id": 1, - "priority_id": 2, - "start_date": formatted_time, - "subject": issue_title, - "description": body - } - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - gitlink_response = requests.post(gitlink_url, headers=gitlink_headers, json=gitlink_payload) - gitee_response = requests.post(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - gitlink_response.raise_for_status() - return {"message": "同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) - - elif data["action"] == "created" and data["issue"]: - # GitHub创建issue评论 - issue_title = data["issue"]["title"] - body = data["comment"]["body"] - github_issue_index = data["issue"]["number"] - - # 在Gitee上创建评论 - gitee_url = f"https://gitee.com/api/v5/repos/{gitee_owner}/{gitee_repo}/issues/{github_issue_index}/comments" - gitee_payload = { - "access_token": gitee_token, - "body": body - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - # 在GitLink上创建issue评论 - gitlink_url = f"https://www.gitlink.org.cn/api/v1/{gitlink_owner}/{gitlink_repo}/issues/{github_issue_index}/journals.json" - gitlink_payload = { - "notes": body, - "receivers_login": ["string"] - } - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', - 'Content-Type': 'application/json' - } - - try: - gitee_response = requests.post(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - gitlink_response = requests.post(gitlink_url, headers=gitlink_headers, json=gitlink_payload) - gitlink_response.raise_for_status() - return {"message": "issue评论同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) - - elif data["action"] == "closed" and data["issue"]: - # GitHub关闭issue - issue_title = data["issue"]["title"] - github_issue_index = data["issue"]["number"] - - # 在GitLink上拒绝该PR - gitlink_url = f"https://www.gitlink.org.cn/api/{gitlink_owner}/{gitlink_repo}/pulls/{github_issue_index}/refuse_merge.json" - gitlink_headers = { - 'Authorization': f'Bearer {gitlink_token}', - 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)' - } - - # 在Gitee上拒绝该PR - gitee_url = f"https://gitee.com/api/v5/repos/{gitee_owner}/{gitee_repo}/issues/{github_issue_index}" - gitee_payload = { - "access_token": gitee_token, - "state": "closed" - } - gitee_headers = { - 'Content-Type': 'application/json;charset=UTF-8' - } - - try: - gitee_response = requests.patch(gitee_url, headers=gitee_headers, json=gitee_payload) - gitee_response.raise_for_status() - gitlink_response = requests.post(gitlink_url, headers=gitlink_headers) - gitlink_response.raise_for_status() - return {"message": "issue关闭同步成功"} - except requests.RequestException as e: - raise HTTPException(status_code=500, detail=str(e)) + # 记录处理时间 + end_time = datetime.now() + process_time = (end_time - start_time).total_seconds() + logger.info("🎉 =========================") + logger.info(f"🎉 处理完成! 耗时: {process_time:.2f}秒") + logger.info("🎉 =========================") - # 在文件的最后,"未开通的服务"返回之前添加日志 - logger.warning("⚠️ =========================") - logger.warning("⚠️ 未开通的服务或未支持的事件") - logger.warning(f"⚠️ 平台: {flag}") - logger.warning(f"⚠️ 动作: {data.get('action', 'unknown')}") - logger.warning("⚠️ =========================") + return { + "status": "success", + "message": "Webhook processed successfully", + "process_time": f"{process_time:.2f}s", + "sync_results": sync_results + } - return {"message": "未开通的服务"} + except Exception as e: + logger.error(f"❌ Webhook处理失败: {str(e)}") + return {"status": "error", "message": f"Webhook processing failed: {str(e)}"} @WEBHOOK.post("/pr") async def pr_webhook(request: Request): @@ -717,7 +524,7 @@ async def pr_webhook(request: Request): logger.info(f"🆔 使用项目ID: {project_id}") # 验证签名 - await verify_webhook_signature(request, body, project_id) + await verify_webhook_signature(request, body, event_info["source"], project_id) # 获取仓库配置 row = await get_sync_repo(project_id) @@ -961,154 +768,4 @@ async def logs_viewer(): ); } - logsContainer.innerHTML = filteredLogs.map(log => ` -