diff --git a/server/core/dao/repositoryConfigDAO.py b/server/core/dao/repositoryConfigDAO.py index 26151aa0..58d6fe01 100644 --- a/server/core/dao/repositoryConfigDAO.py +++ b/server/core/dao/repositoryConfigDAO.py @@ -1,9 +1,8 @@ -from typing import List +from typing import Counter, List from core.dao.BaseDAO import BaseDAO from core.models.bot import RepoBindBotConfigVO from core.models.repository import RepositoryConfig from supabase.client import Client - from petercat_utils.db.client.supabase import get_client @@ -26,65 +25,120 @@ def create(self, data: RepositoryConfig): else: return False, {"message": "GithubRepoConfig creation failed"} except Exception as e: - print("Error: ", e) - return False, {"message": "GithubRepoConfig creation failed"} + print(f"Error: {e}") + return False, {"message": f"GithubRepoConfig creation failed: {e}"} + + def create_batch(self, data_list: List[RepositoryConfig]): + try: + records_data = [data.model_dump(exclude=["id"]) for data in data_list] + repo_ids = [data.repo_id for data in data_list] + + # 查询现有的 repo_id + response = ( + self.client.table("github_repo_config") + .select("repo_id") + .in_("repo_id", repo_ids) + .execute() + ) + existing_repo_ids = ( + {record["repo_id"] for record in response.data} + if response.data + else set() + ) - def query_by_owners(self, orgs: list[str]): - response = ( - self.client.table("github_repo_config") - .select("*") - .filter("owner_id", "in", f"({','.join(map(str, orgs))})") - .execute() - ) + # 筛选出未存在的记录 + new_records_data = [ + data + for data in records_data + if data["repo_id"] not in existing_repo_ids + ] - return response.data + if not new_records_data: + return False, { + "message": "No new GithubRepoConfig records to insert, all repo_ids already exist" + } - def update_bot_to_repos( - self, - repos: List[RepoBindBotConfigVO], - ) -> bool: - for repo in repos: - res = ( + # 执行插入操作 + repo_config_result = ( self.client.table("github_repo_config") - .update({"robot_id": repo.robot_id}) - .match({"repo_id": repo.repo_id}) + .insert(new_records_data) .execute() ) - if not res: - raise ValueError("Failed to bind the bot.") - def get_by_repo_name(self, repo_name: str): - response = ( - self.client.table("github_repo_config") - .select("*") - .eq("repo_name", repo_name) - .execute() - ) + return repo_config_result + except Exception as e: + print(f"Error: {e}") + return False, {"message": f"GithubRepoConfig batch creation failed: {e}"} - if not response.data or not response.data[0]: + def query_by_owners(self, orgs: List[str]): + try: + response = ( + self.client.table("github_repo_config") + .select("*") + .filter("owner_id", "in", f"({','.join(map(str, orgs))})") + .execute() + ) + return response.data + except Exception as e: + print(f"Error: {e}") return None - repo_config = response.data[0] - return RepositoryConfig(**repo_config) + def update_bot_to_repos(self, repos: List[RepoBindBotConfigVO]) -> bool: + try: + for repo in repos: + res = ( + self.client.table("github_repo_config") + .update({"robot_id": repo.robot_id}) + .match({"repo_id": repo.repo_id}) + .execute() + ) + if not res: + raise ValueError("Failed to bind the bot.") + return True + except Exception as e: + print(f"Error: {e}") + return False - def get_by_bot_id(self, bot_id: str): - response = ( - self.client.table("github_repo_config") - .select("*") - .eq("robot_id", bot_id) - .execute() - ) - if not response.data or not response.data[0]: + def get_by_repo_name(self, repo_name: str): + try: + response = ( + self.client.table("github_repo_config") + .select("*") + .eq("repo_name", repo_name) + .execute() + ) + if not response.data or not response.data[0]: + return None + repo_config = response.data[0] + return RepositoryConfig(**repo_config) + except Exception as e: + print(f"Error: {e}") return None - repo_configs = [RepositoryConfig(**repo) for repo in response.data] - return repo_configs + def get_by_bot_id(self, bot_id: str): + try: + response = ( + self.client.table("github_repo_config") + .select("*") + .eq("robot_id", bot_id) + .execute() + ) + if not response.data or not response.data[0]: + return None + return [RepositoryConfig(**repo) for repo in response.data] + except Exception as e: + print(f"Error: {e}") + return None - def delete_by_repo_ids(self, repo_ids: list): - response = ( - self.client.table("github_repo_config") - .delete() - .in_("repo_id", repo_ids) - .execute() - ) - return response + def delete_by_repo_ids(self, repo_ids: List[str]): + try: + response = ( + self.client.table("github_repo_config") + .delete() + .in_("repo_id", repo_ids) + .execute() + ) + return response + except Exception as e: + print(f"Error: {e}") + return None diff --git a/server/event_handler/intsall.py b/server/event_handler/intsall.py index 7e843602..757fd739 100644 --- a/server/event_handler/intsall.py +++ b/server/event_handler/intsall.py @@ -1,10 +1,12 @@ -from typing import Any +from typing import Any, List from github import Github, Auth from github import GithubException from core.dao.repositoryConfigDAO import RepositoryConfigDAO +from core.models.repository import RepositoryConfig +import time -class InstallEventHandler: +class InstallationEventHandler: event: Any auth: Auth.AppAuth g: Github @@ -20,12 +22,77 @@ def delete_config(self): repository_config = RepositoryConfigDAO() repository_config.delete_by_repo_ids(repo_ids) + def add_config(self): + repositories = self.event["repositories"] + owner_id = self.event["installation"]["account"]["id"] + repository_config_dao = RepositoryConfigDAO() + repository_configs: List[RepositoryConfig] = [] + for repo in repositories: + repository_config = RepositoryConfig( + owner_id=str(owner_id), + repo_name=repo["full_name"], + repo_id=str(repo["id"]), + robot_id="", + created_at=int(time.time()), + ) + repository_configs.append(repository_config) + repository_config_dao.create_batch(repository_configs) + async def execute(self): try: action = self.event["action"] if action == "deleted": self.delete_config() return {"success": True} + if action == "created": + self.add_config() + return {"success": True} + except GithubException as e: + print(f"处理 GitHub 请求时出错:{e}") + return {"success": False, "error": str(e)} + + +class InstallationEditEventHandler: + event: Any + auth: Auth.AppAuth + g: Github + + def __init__(self, payload: Any, auth: Auth.AppAuth, installation_id: int) -> None: + self.event: Any = payload + self.auth: Auth.AppAuth = auth + self.g: Github = Github(auth=auth) + + def delete_config(self): + repositories = self.event["repositories_removed"] + repo_ids = [str(repo["id"]) for repo in repositories] + repository_config = RepositoryConfigDAO() + repository_config.delete_by_repo_ids(repo_ids) + + def add_config(self): + repositories = self.event["repositories_added"] + owner_id = self.event["installation"]["account"]["id"] + repository_config_dao = RepositoryConfigDAO() + repository_configs: List[RepositoryConfig] = [] + for repo in repositories: + repository_config = RepositoryConfig( + owner_id=str(owner_id), + repo_name=repo["full_name"], + repo_id=str(repo["id"]), + robot_id="", + created_at=int(time.time()), + ) + repository_configs.append(repository_config) + repository_config_dao.create_batch(repository_configs) + + async def execute(self): + try: + action = self.event["action"] + if action == "removed": + self.delete_config() + return {"success": True} + if action == "added": + self.add_config() + return {"success": True} except GithubException as e: print(f"处理 GitHub 请求时出错:{e}") return {"success": False, "error": str(e)} diff --git a/server/github_app/handlers.py b/server/github_app/handlers.py index 3caea4ad..0581221f 100644 --- a/server/github_app/handlers.py +++ b/server/github_app/handlers.py @@ -1,6 +1,6 @@ from typing import Union -from event_handler.intsall import InstallEventHandler +from event_handler.intsall import InstallationEventHandler, InstallationEditEventHandler from petercat_utils import get_env_variable from github import Auth @@ -26,7 +26,8 @@ def get_handler( DiscussionEventHandler, DiscussionCommentEventHandler, PullRequestReviewCommentEventHandler, - InstallEventHandler, + InstallationEventHandler, + InstallationEditEventHandler, None, ]: handlers = { @@ -37,7 +38,8 @@ def get_handler( "discussion_comment": DiscussionCommentEventHandler, "pull_request_review_comment": PullRequestReviewCommentEventHandler, "pull_request_review": PullRequestReviewCommentEventHandler, - "installation": InstallEventHandler, + "installation": InstallationEventHandler, + "installation_repositories": InstallationEditEventHandler, } return ( handlers.get(event)(payload=payload, auth=auth, installation_id=installation_id) diff --git a/server/github_app/router.py b/server/github_app/router.py index 411bb676..c8443d25 100644 --- a/server/github_app/router.py +++ b/server/github_app/router.py @@ -50,48 +50,10 @@ # https://github.com/login/oauth/authorize?client_id=Iv1.c2e88b429e541264 @router.get("/app/installation/callback") def github_app_callback(code: str, installation_id: str, setup_action: str): - authorization_dao = AuthorizationDAO() - repository_config_dao = RepositoryConfigDAO() - if setup_action == "install": - if authorization_dao.exists(installation_id=installation_id): - message = (f"Installation_id {installation_id} Exists",) - return RedirectResponse( - url=f"{WEB_URL}/github/installed/{message}", status_code=302 - ) - else: - jwt = get_jwt() - access_token = get_app_installations_access_token( - installation_id=installation_id, jwt=jwt - ) - print(f"get_app_installations_access_token: {access_token}") - authorization = Authorization( - **access_token, - code=code, - installation_id=installation_id, - created_at=int(time.time()), - ) - success, message = authorization_dao.create(authorization) - installed_repositories = get_installation_repositories( - access_token=access_token["token"] - ) - for repo in installed_repositories["repositories"]: - repository_config = RepositoryConfig( - owner_id=str(repo["owner"]["id"]), - repo_name=repo["full_name"], - repo_id=str(repo["id"]), - robot_id="", - created_at=int(time.time()), - ) - repository_config_dao.create(repository_config) - - return RedirectResponse( - url=f"{WEB_URL}/github/installed?message={message}", status_code=302 - ) - # ignore others setup_action,such as deleted our app - return { - "success": False, - "message": f"Invalid setup_action value {setup_action},please delete the app first then re-install the app.", - } + return RedirectResponse( + url=f"{WEB_URL}/github/installed?installation_id={installation_id}&setup_action={setup_action}&code={code}", + status_code=302, + ) @router.post("/app/webhook")