diff --git a/server/agent/bot_builder.py b/server/agent/bot_builder.py
index 32ccacd5..c0c5fdc8 100644
--- a/server/agent/bot_builder.py
+++ b/server/agent/bot_builder.py
@@ -3,8 +3,8 @@
from petercat_utils.data_class import ChatData
from agent.base import AgentBuilder
-from prompts.bot_builder import generate_prompt_by_user_id
-from tools import bot_builder
+from agent.prompts.bot_builder import generate_prompt_by_user_id
+from agent.tools import bot_builder
TOOL_MAPPING = {
diff --git a/server/prompts/bot_builder.py b/server/agent/prompts/bot_builder.py
similarity index 100%
rename from server/prompts/bot_builder.py
rename to server/agent/prompts/bot_builder.py
diff --git a/server/prompts/bot_template.py b/server/agent/prompts/bot_template.py
similarity index 100%
rename from server/prompts/bot_template.py
rename to server/agent/prompts/bot_template.py
diff --git a/server/prompts/repo_pr.py b/server/agent/prompts/repo_pr.py
similarity index 100%
rename from server/prompts/repo_pr.py
rename to server/agent/prompts/repo_pr.py
diff --git a/server/agent/qa_chat.py b/server/agent/qa_chat.py
index d0d47d69..bff0c681 100644
--- a/server/agent/qa_chat.py
+++ b/server/agent/qa_chat.py
@@ -1,12 +1,12 @@
from typing import AsyncIterator, Optional
from agent.base import AgentBuilder
from agent.llm import get_llm
-from dao.botDAO import BotDAO
-from models.bot import Bot
-from prompts.bot_template import generate_prompt_by_repo_name
+from core.dao.botDAO import BotDAO
+from core.models.bot import Bot
+from agent.prompts.bot_template import generate_prompt_by_repo_name
from petercat_utils.data_class import ChatData
-from tools import issue, sourcecode, knowledge, git_info
+from agent.tools import issue, sourcecode, knowledge, git_info
def get_tools(bot: Bot, token: Optional[str]):
diff --git a/server/tools/bot_builder.py b/server/agent/tools/bot_builder.py
similarity index 100%
rename from server/tools/bot_builder.py
rename to server/agent/tools/bot_builder.py
diff --git a/server/tools/git_info.py b/server/agent/tools/git_info.py
similarity index 100%
rename from server/tools/git_info.py
rename to server/agent/tools/git_info.py
diff --git a/server/tools/helper.py b/server/agent/tools/helper.py
similarity index 100%
rename from server/tools/helper.py
rename to server/agent/tools/helper.py
diff --git a/server/tools/issue.py b/server/agent/tools/issue.py
similarity index 98%
rename from server/tools/issue.py
rename to server/agent/tools/issue.py
index 07a2411c..0111f4cb 100644
--- a/server/tools/issue.py
+++ b/server/agent/tools/issue.py
@@ -3,7 +3,7 @@
from github import Auth, Github
from langchain.tools import tool
-from tools.helper import need_github_login
+from agent.tools.helper import need_github_login
DEFAULT_REPO_NAME = "ant-design/ant-design"
diff --git a/server/tools/knowledge.py b/server/agent/tools/knowledge.py
similarity index 100%
rename from server/tools/knowledge.py
rename to server/agent/tools/knowledge.py
diff --git a/server/tools/sourcecode.py b/server/agent/tools/sourcecode.py
similarity index 100%
rename from server/tools/sourcecode.py
rename to server/agent/tools/sourcecode.py
diff --git a/server/verify/rate_limit.py b/server/auth/rate_limit.py
similarity index 100%
rename from server/verify/rate_limit.py
rename to server/auth/rate_limit.py
diff --git a/server/routers/auth.py b/server/auth/router.py
similarity index 100%
rename from server/routers/auth.py
rename to server/auth/router.py
diff --git a/server/bot/builder.py b/server/bot/builder.py
index 35603ac8..a1884508 100644
--- a/server/bot/builder.py
+++ b/server/bot/builder.py
@@ -5,7 +5,7 @@
from petercat_utils.data_class import RAGGitDocConfig
from petercat_utils import git_doc_task
-from prompts.bot_template import generate_prompt_by_repo_name
+from agent.prompts.bot_template import generate_prompt_by_repo_name
g = Github()
diff --git a/server/routers/bot.py b/server/bot/router.py
similarity index 98%
rename from server/routers/bot.py
rename to server/bot/router.py
index e4dd2437..2b405926 100644
--- a/server/routers/bot.py
+++ b/server/bot/router.py
@@ -5,7 +5,7 @@
from typing import Annotated, Optional
from bot.builder import bot_builder, bot_info_generator
-from type_class.bot import BotUpdateRequest, BotCreateRequest
+from core.type_class.bot import BotUpdateRequest, BotCreateRequest
router = APIRouter(
prefix="/api/bot",
diff --git a/server/routers/chat.py b/server/chat/router.py
similarity index 97%
rename from server/routers/chat.py
rename to server/chat/router.py
index ed865b11..c1bb740f 100644
--- a/server/routers/chat.py
+++ b/server/chat/router.py
@@ -4,7 +4,7 @@
from petercat_utils.data_class import ChatData
from agent import qa_chat, bot_builder
-from verify.rate_limit import verify_rate_limit
+from auth.rate_limit import verify_rate_limit
from auth.get_user_info import get_user_access_token, get_user_id
diff --git a/server/dao/BaseDAO.py b/server/core/dao/BaseDAO.py
similarity index 100%
rename from server/dao/BaseDAO.py
rename to server/core/dao/BaseDAO.py
diff --git a/server/dao/authorizationDAO.py b/server/core/dao/authorizationDAO.py
similarity index 93%
rename from server/dao/authorizationDAO.py
rename to server/core/dao/authorizationDAO.py
index 9fff5100..4964f169 100644
--- a/server/dao/authorizationDAO.py
+++ b/server/core/dao/authorizationDAO.py
@@ -2,8 +2,8 @@
from petercat_utils.db.client.supabase import get_client
-from dao.BaseDAO import BaseDAO
-from models.authorization import Authorization
+from core.dao.BaseDAO import BaseDAO
+from core.models.authorization import Authorization
class AuthorizationDAO(BaseDAO):
client: Client
diff --git a/server/dao/botDAO.py b/server/core/dao/botDAO.py
similarity index 85%
rename from server/dao/botDAO.py
rename to server/core/dao/botDAO.py
index fc6e56c6..2561adbd 100644
--- a/server/dao/botDAO.py
+++ b/server/core/dao/botDAO.py
@@ -1,7 +1,7 @@
-from dao.BaseDAO import BaseDAO
+from core.dao.BaseDAO import BaseDAO
from supabase.client import Client
-from models.bot import Bot
+from core.models.bot import Bot
from petercat_utils.db.client.supabase import get_client
class BotDAO(BaseDAO):
diff --git a/server/dao/repositoryConfigDAO.py b/server/core/dao/repositoryConfigDAO.py
similarity index 93%
rename from server/dao/repositoryConfigDAO.py
rename to server/core/dao/repositoryConfigDAO.py
index fff0e68a..9ef0b7c8 100644
--- a/server/dao/repositoryConfigDAO.py
+++ b/server/core/dao/repositoryConfigDAO.py
@@ -1,6 +1,6 @@
-from dao.BaseDAO import BaseDAO
-from models.repository import RepositoryConfig
+from core.dao.BaseDAO import BaseDAO
+from core.models.repository import RepositoryConfig
from supabase.client import Client
from petercat_utils.db.client.supabase import get_client
diff --git a/server/models/authorization.py b/server/core/models/authorization.py
similarity index 100%
rename from server/models/authorization.py
rename to server/core/models/authorization.py
diff --git a/server/core/models/bot.py b/server/core/models/bot.py
new file mode 100644
index 00000000..750e07a8
--- /dev/null
+++ b/server/core/models/bot.py
@@ -0,0 +1,13 @@
+from datetime import datetime
+from typing import Optional
+from pydantic import BaseModel
+
+class Bot(BaseModel):
+ id: str
+ uid: str
+ avatar: Optional[str] = ""
+ description: Optional[str]
+ prompt: Optional[str] = ""
+ name: str
+ llm: Optional[str] = "openai"
+ created_at: datetime
\ No newline at end of file
diff --git a/server/models/repository.py b/server/core/models/repository.py
similarity index 100%
rename from server/models/repository.py
rename to server/core/models/repository.py
diff --git a/server/type_class/bot.py b/server/core/type_class/bot.py
similarity index 100%
rename from server/type_class/bot.py
rename to server/core/type_class/bot.py
diff --git a/server/docs/test.md b/server/docs/test.md
deleted file mode 100644
index 03e1f8f1..00000000
--- a/server/docs/test.md
+++ /dev/null
@@ -1,197 +0,0 @@
----
-order: 1
-title: 资源
-description: 这里汇总了与 Ant Design 相关的所有资源。
-toc: false
----
-
-## 设计资源
-
-这里提供 Ant Design 相关设计资源和设计工具的下载,更多设计资源正在整理和完善中。你可以在这个[地址](https://www.yuque.com/kitchen/topics/216)中反馈对新版本 Sketch Symbols 组件的意见。
-
-
-
-
-- Sketch 组件包
- - https://gw.alipayobjects.com/zos/basement_prod/048ee28f-2c80-4d15-9aa3-4f5ddac50465.svg
- - 桌面组件 Sketch 模板包
- - https://github.com/ant-design/ant-design/releases/download/5.13.3/AntDesign5.0_UI.KIT_202401.sketch
- - 官方
-- Mobile Components
- - https://gw.alipayobjects.com/zos/basement_prod/c0c3852c-d245-4330-886b-cb02ef49eb6d.svg
- - 移动组件 Sketch 模板
- - https://gw.alipayobjects.com/os/bmw-prod/d6266aef-25b7-4892-b275-ce214121831c.sketch
- - 官方
-- Ant Design Pro
- - https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
- - 典型页面 + 通用业务模板
- - https://gw.alipayobjects.com/os/bmw-prod/22208f9d-f8c5-4d7c-b87a-fec290e96527.sketch
- - 官方
-- Kitchen
- - https://gw.alipayobjects.com/zos/basement_prod/d475d063-2754-4442-b9db-5d164e06acc9.svg
- - Sketch 工具集
- - http://kitchen.alipay.com
- - 官方
-- Ant Design Landing
- - https://gw.alipayobjects.com/zos/basement_prod/b443f4be-5116-49b7-873f-a7c8502b8f0e.svg
- - 首页模板集
- - https://landing.ant.design/docs/download-cn
- - 官方
-- Ant Design 原型 (xiaopiu)
- - https://gw.alipayobjects.com/zos/basement_prod/77e6a9ae-24a9-4be6-be42-f7fa8ee0eecf.svg
- - 可在线编辑的组件库和交互原型
- - https://www.xiaopiu.com/topic/ant-design
-- Figma 组件包
- - https://gw.alipayobjects.com/zos/basement_prod/7b9ed3f2-6f05-4ddb-bac3-d55feb71e0ac.svg
- - 在 Figma 使用 Ant Design 进行设计
- - https://www.antforfigma.com
-- Figma 开源组件包
- - https://gw.alipayobjects.com/zos/basement_prod/7b9ed3f2-6f05-4ddb-bac3-d55feb71e0ac.svg
- - 代码级精确度的免费开源 Figma 完全组件库
- - https://www.figma.com/community/file/831698976089873405
-- 如意设计助手
- - https://github.com/ant-design/ant-design/assets/507615/45201521-37d0-4360-b81e-a1260dedad7a
- - Figma 插件,使用 antd 代码组件库进行设计,交付对开发者友好的组件代码
- - https://www.figma.com/community/plugin/1192146318523533547
-- 全新 Chart 组件包
- - https://gw.alipayobjects.com/zos/basement_prod/a9dc586a-fe0a-4c7d-ab4f-f5ed779b963d.svg
- - 桌面组件 Chart 模板包
- - https://gw.alipayobjects.com/os/bmw-prod/704968a5-2641-484e-9f65-c2735b2c0287.sketch
- - 官方
-- 墨刀原型设计
- - https://cdn.modao.cc/logo_mockingbot.svg
- - 内置丰富的 Ant Design 组件资源
- - https://modao.cc/square/ant-design
-- 全套资源包(即时设计)
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*dxzdQYWlmjMAAAAAAAAAAAAAARQnAQ
- - 可在「即时设计」在线免费使用的全套组件和模板
- - https://js.design/antd
-- MasterGo 组件包
- - https://mastergo-local-default.oss-cn-beijing.aliyuncs.com/ant-design-mastergo.svg
- - 可在「MasterGo」在线免费使用的全套组件和模板
- - https://mastergo.com/community/?utm_source=antdesign&utm_medium=link&utm_campaign=resource&cata_name=AntDesign
-- Raycast 拓展
- - https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
- - mac 用户可使用 Raycast 快速打开 Ant Design 组件
- - https://www.raycast.com/crazyair/antd-open-browser
-
-
-## 文章
-
-想要了解 Ant Design 设计体系背后的故事?如何才能更好的应用 Ant Design?你可以查阅下述我们为你精挑细选的文章。也欢迎关注 [Ant Design 官方专栏](https://www.zhihu.com/column/c_1310524851418480640),这里常有关于 Ant Design 设计体系下相关话题内容的最新分享和讨论,如 Ant Design、AntV 可视化、Kitchen 设计插件、B 端产品设计、SaaS 产品设计、自然交互、增长设计、智能设计、设计工程化等。
-
-
-
-## 致敬
-
-在 Ant Design 4.0 的改版中,我们汲取顶级设计体系的精华,同时结合我们自身业务特性做了大量优化。我们希望通过不断努力和打磨,成为世界级设计体系的一份子,为「用户」和「设计者」带来极致体验。如果你也想追求卓越,建议去研究这些体系: [Fiori Design](https://experience.sap.com/fiori-design-web/)、 [Human Interface Guidelines](https://developer.apple.com/ios/human-interface-guidelines/overview/themes/)、 [Lightning Design System](https://lightningdesignsystem.com/getting-started/)、 [Material Design](https://material.io/)
-
-
-
-
-- About Face 4 #E1E8B7
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*KKZWRozT8D8AAAAAAAAAAABkARQnAQ
- - 一本数字产品和系统的交互设计指南
- - http://book.douban.com/subject/26642302/
-- Web 界面设计 #009C94
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*yB0oQ42f0kEAAAAAAAAAAABkARQnAQ
- - Web 界面的最佳实践、模式和原理
- - http://book.douban.com/subject/3821157/
-- 界面设计模式 #9489CF
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*aFAfTKIjR_IAAAAAAAAAAABkARQnAQ
- - 界面设计总体思路指引
- - http://book.douban.com/subject/25716088/
-- 写给大家看的设计书 #AFBCC8
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*tTvXQYApsIIAAAAAAAAAAABkARQnAQ
- - 优秀设计所必须遵循的基本原则
- - http://book.douban.com/subject/3323633/
-- 设计心理学 1 #B7D9B7
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*12W8R7nPxxUAAAAAAAAAAABkARQnAQ
- - 强调以人为本的设计哲学
- - http://book.douban.com/subject/26102860/
-- 设计心理学 3 #EFBDB5
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*a5VNSamt2EIAAAAAAAAAAABkARQnAQ
- - 解释情感因素在设计领域扮演的角色
- - http://book.douban.com/subject/26424688/
-- Web 表单设计 #C2DAED
- - https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*vXfQS7sStNYAAAAAAAAAAABkARQnAQ
- - 表单设计的真谛
- - http://book.douban.com/subject/4886100/
-
-
-## 加入我们
-
-蚂蚁集团 Ant Design 团队是一支兼具设计视角和工程视角的横向组织,服务蚂蚁集团上百个中后台系统,主打产品 Ant Design 服务全球 100 万设计师和工程师,是西湖区学院路西侧最具影响力的设计语言。欢迎来这里和我们一起打造优雅高效的人机设计/研发体系。
-
-### UI/UE 设计师
-
-简历和作品集请投递:jiayin.liu#antgroup.com
-
-> 注明简历来自 ant.design 官网
-
-- 岗位级别:P5/P6/P7/P8
-- 岗位地点:杭州
-- 岗位要求:
- - 至少 3-5 年的工作经验,扎实设计功底;
- - 抽象能力强,善于透过表象找本质;
- - 沟通能力佳,善于自我管理;
- - 有企业级设计实战经验,加分;
- - 有数据驱动的增长设计实践,加分;
- - 深度理解 SAP、Salesforce、Google 等设计体系,能提出自己独到见解并落实到实践中,加加加分。
-- 岗位职责:
- - 参与[蚂蚁链](https://blockchain.antgroup.com/)、人工智能、数据平台等企业级产品的设计工作;
- - 参与[语雀](https://www.yuque.com/) 等创新产品的设计工作;
- - 参与 Ant Design 的打磨,将其建设成全球卓越的设计体系。
- - 参与 AntV 的打磨,将其建设成全球一流的数据可视化体系。
-- One More Thing ❤️ :
- - 你们总是为世界带去美好,但总是忘却你们也需要美好。我们正在努力打造 [🍳 Kitchen:一款为设计师提效的 Sketch 工具集](https://kitchen.alipay.com/)等专属设计师的产品,让设计真正变成财富。期待志同道合的你,一道给设计行业带来「微小而美好的改变」。
-
-### 前端工程师
-
-简历请投递:afc163+antd@gmail.com
-
-> 注明简历来自 ant.design 官网
-
-- 岗位级别:P5/P6/P7/P8
-- 岗位地点:杭州/上海
-- 岗位要求:
- - 在 React 技术栈持续耕耘,情有独钟。
- - 热爱开源。
- - 坚持和善于用技术和工具解决其他问题。
- - 丰富的中后台前端研发经验。
-- 岗位职责:
- - 负责 Ant Design 前端基础设施研发。
- - 负责中后台设计/前端工具体系建设。
-
-### Node.js 工程师
-
-简历请投递:zhubin.gzb@antgroup.com
-
-> 注明简历来自 ant.design 官网
-
-- 岗位级别:P5/P6/P7/P8
-- 岗位地点:杭州/上海
-- 岗位要求:
- - 在 Node.js 技术栈持续耕耘,情有独钟。
- - 热爱开源。
- - 坚持和善于用技术和工具解决其他问题。
- - 丰富的 Node.js 研发经验。
-- 岗位职责:
- - 负责 Node.js 前端基础设施研发。
- - 负责大前端工具体系建设。
-
-### ADI(Artificial Design Intelligence) 工程师
-
-简历和作品集请投递:jiayin.liu#antgroup.com
-
-> 注明简历来自 ant.design 官网
-
-- 岗位级别:P7/P8
-- 岗位地点:杭州
-- 岗位要求:
- - 有良好的工程师背景,善于学习和使用各类工具、框架解决研发问题;
- - 对人工智能应用在设计行业,有坚定的信心和意愿;
- - 已经有相关实践工作,优先考虑。
-- 岗位职责:
- - 负责 Ant Design 工具体系和智能设计的研发,并配合团队成员进行商业化实践,把设计做成业务;
- - 组建和培养有梯度的研发团队。
diff --git a/server/event_handler/issue.py b/server/event_handler/issue.py
index f5b59a19..f595e4f3 100644
--- a/server/event_handler/issue.py
+++ b/server/event_handler/issue.py
@@ -2,7 +2,7 @@
from github import Github, Auth
from github import GithubException
-from dao.repositoryConfigDAO import RepositoryConfigDAO
+from core.dao.repositoryConfigDAO import RepositoryConfigDAO
from petercat_utils.data_class import ChatData, Message, TextContentBlock
from agent.qa_chat import agent_chat
diff --git a/server/utils/github.py b/server/github_app/handlers.py
similarity index 53%
rename from server/utils/github.py
rename to server/github_app/handlers.py
index 71a51c83..0b29aa73 100644
--- a/server/utils/github.py
+++ b/server/github_app/handlers.py
@@ -1,6 +1,5 @@
from typing import Union
-import boto3
-from botocore.exceptions import ClientError
+
from petercat_utils import get_env_variable
from github import Auth
@@ -10,24 +9,6 @@
APP_ID = get_env_variable("X_GITHUB_APP_ID")
-def get_private_key():
- secret_name = "prod/githubapp/petercat/pem"
- region_name = "ap-northeast-1"
- session = boto3.session.Session()
- client = session.client(
- service_name='secretsmanager',
- region_name=region_name
- )
- try:
- get_secret_value_response = client.get_secret_value(
- SecretId=secret_name
- )
- except ClientError as e:
- # For a list of exceptions thrown, see
- # https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
- raise e
-
- return get_secret_value_response['SecretString']
def get_handler(event: str, payload: dict, auth: Auth.AppAuth, installation_id: int) -> Union[PullRequestEventHandler, IssueEventHandler, DiscussionEventHandler, None]:
handlers = {
diff --git a/server/github_app/router.py b/server/github_app/router.py
new file mode 100644
index 00000000..15e58b64
--- /dev/null
+++ b/server/github_app/router.py
@@ -0,0 +1,124 @@
+from typing import Annotated
+from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Header, Request, status
+import logging
+from fastapi.responses import RedirectResponse
+
+import time
+from github import Auth, Github
+from auth.get_user_info import get_user_access_token
+from core.dao.authorizationDAO import AuthorizationDAO
+from core.dao.repositoryConfigDAO import RepositoryConfigDAO
+from core.models.repository import RepositoryConfig
+from core.models.authorization import Authorization
+
+from github_app.handlers import get_handler
+from github_app.utils import get_app_installations_access_token, get_installation_repositories, get_jwt, get_private_key
+
+from petercat_utils import get_env_variable
+
+
+APP_ID = get_env_variable("X_GITHUB_APP_ID")
+WEB_URL = get_env_variable("WEB_URL")
+
+logger = logging.getLogger()
+logger.setLevel("INFO")
+
+router = APIRouter(
+ prefix="/api/github",
+ tags=["github"],
+ responses={404: {"description": "Not found"}},
+)
+
+
+# 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":
+ return {
+ "success": False,
+ "message": f"Invalid setup_action value {setup_action}"
+ }
+ elif authorization_dao.exists(installation_id=installation_id):
+ return {
+ "success": False,
+ "message": f"Installation_id {installation_id} Exists"
+ }
+ 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)
+ print(f"github_app_callback: success={success}, message={message}")
+
+ installed_repositories = get_installation_repositories(
+ access_token=access_token['token']
+ )
+ for repo in installed_repositories["repositories"]:
+ repository_config = RepositoryConfig(
+ repo_name=repo["full_name"],
+ 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
+ )
+
+@router.post("/app/webhook")
+async def github_app_webhook(
+ request: Request,
+ background_tasks: BackgroundTasks,
+ x_github_event: str = Header(...),
+):
+ payload = await request.json()
+ if "installation" not in payload:
+ return {"success": False, "message": "Invalid Webhook request"}
+
+ installation_id = payload["installation"]["id"]
+ try:
+ auth = Auth.AppAuth(
+ app_id=APP_ID, private_key=get_private_key(), jwt_algorithm="RS256"
+ ).get_installation_auth(installation_id=int(installation_id))
+ except Exception as e:
+ print("Failed", f"Authentication failed: {e}")
+ return {"success": False, "message": f"Authentication failed: {e}"}
+
+ handler = get_handler(
+ x_github_event, payload, auth, installation_id=installation_id
+ )
+ if handler:
+ await handler.execute()
+ return {"success": True}
+ else:
+ print("Failed, Unsupported GitHub event")
+ return {"success": False, "message": "Unsupported GitHub event"}
+
+@router.get("/user/organizations")
+async def get_user_organizations(
+ user_access_token: Annotated[str | None, Depends(get_user_access_token)] = None
+):
+ if user_access_token is None:
+ raise HTTPException(
+ status_code=status.HTTP_401_UNAUTHORIZED,
+ detail="Github Login needed"
+ )
+ auth = Auth.Token(token=user_access_token)
+ g = Github(auth=auth)
+ user = g.get_user()
+ orgs = user.get_orgs()
+
+ return [org.raw_data for org in orgs]
diff --git a/server/github_app/utils.py b/server/github_app/utils.py
new file mode 100644
index 00000000..06e7b0af
--- /dev/null
+++ b/server/github_app/utils.py
@@ -0,0 +1,70 @@
+import boto3
+import jwt
+import requests
+from botocore.exceptions import ClientError
+import time
+
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.backends import default_backend
+
+from petercat_utils.utils.env import get_env_variable
+
+APP_ID = get_env_variable("X_GITHUB_APP_ID")
+
+def get_private_key():
+ secret_name = "prod/githubapp/petercat/pem"
+ region_name = "ap-northeast-1"
+ session = boto3.session.Session()
+ client = session.client(
+ service_name='secretsmanager',
+ region_name=region_name
+ )
+ try:
+ get_secret_value_response = client.get_secret_value(
+ SecretId=secret_name
+ )
+ except ClientError as e:
+ # For a list of exceptions thrown, see
+ # https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
+ raise e
+
+ return get_secret_value_response['SecretString']
+
+def get_jwt():
+ payload = {
+ # Issued at time
+ 'iat': int(time.time()),
+ # JWT expiration time (10 minutes maximum)
+ 'exp': int(time.time()) + 600,
+ # GitHub App's identifier
+ 'iss': APP_ID
+ }
+
+ pem = get_private_key()
+ private_key = serialization.load_pem_private_key(
+ pem.encode("utf-8"), password=None, backend=default_backend()
+ )
+ return jwt.encode(payload, private_key, algorithm='RS256')
+
+def get_app_installations_access_token(installation_id: str, jwt: str):
+ url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
+ print("get_app_installations_access_token", url, jwt)
+ resp = requests.post(url,
+ headers={
+ 'X-GitHub-Api-Version': '2022-11-28',
+ 'Accept': 'application/vnd.github+json',
+ 'Authorization': f"Bearer {jwt}"
+ }
+ )
+
+ return resp.json()
+
+def get_installation_repositories(access_token: str):
+ url = "https://api.github.com/installation/repositories"
+ print("get_installation_repositories", url)
+ resp = requests.get(url, headers={
+ 'X-GitHub-Api-Version': '2022-11-28',
+ 'Accept': 'application/vnd.github+json',
+ 'Authorization': f"Bearer {access_token}"
+ })
+ return resp.json()
\ No newline at end of file
diff --git a/server/main.py b/server/main.py
index 296457c1..e0c56b58 100644
--- a/server/main.py
+++ b/server/main.py
@@ -10,7 +10,12 @@
# Import fastapi routers
-from routers import bot, health_checker, github, rag, auth, chat, task
+from auth import router as auth_router
+from bot import router as bot_router
+from chat import router as chat_router
+from rag import router as rag_router
+from task import router as task_router
+from github_app import router as github_app_router
AUTH0_DOMAIN = get_env_variable("AUTH0_DOMAIN")
API_AUDIENCE = get_env_variable("API_IDENTIFIER")
@@ -43,13 +48,17 @@
)
-app.include_router(health_checker.router)
-app.include_router(github.router)
-app.include_router(rag.router)
-app.include_router(bot.router)
-app.include_router(auth.router)
-app.include_router(chat.router)
-app.include_router(task.router)
+app.include_router(rag_router.router)
+app.include_router(bot_router.router)
+app.include_router(auth_router.router)
+app.include_router(chat_router.router)
+app.include_router(task_router.router)
+app.include_router(github_app_router.router)
+
+
+@app.get("/api/health_checker")
+def health_checker():
+ return { "Hello": "World" }
if __name__ == "__main__":
diff --git a/server/models/bot.py b/server/models/bot.py
deleted file mode 100644
index 79fa139f..00000000
--- a/server/models/bot.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from datetime import datetime
-from pydantic import BaseModel
-
-class Bot(BaseModel):
- id: str
- uid: str
- avatar: str
- description: str
- prompt: str
- name: str
- llm: str
- created_at: datetime
\ No newline at end of file
diff --git a/server/routers/rag.py b/server/rag/router.py
similarity index 98%
rename from server/routers/rag.py
rename to server/rag/router.py
index a919f11c..91e4f1dd 100644
--- a/server/routers/rag.py
+++ b/server/rag/router.py
@@ -13,7 +13,7 @@
git_issue_task,
)
-from verify.rate_limit import verify_rate_limit
+from auth.rate_limit import verify_rate_limit
router = APIRouter(
diff --git a/server/routers/__init__.py b/server/routers/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/server/routers/github.py b/server/routers/github.py
deleted file mode 100644
index 0dc4e8e8..00000000
--- a/server/routers/github.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from typing import Annotated
-from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Header, Request, status
-import logging
-from fastapi.responses import RedirectResponse
-import requests
-import time
-from github import Auth, Github
-from auth.get_user_info import get_user_access_token
-from dao.authorizationDAO import AuthorizationDAO
-from dao.repositoryConfigDAO import RepositoryConfigDAO
-from models.repository import RepositoryConfig
-from models.authorization import Authorization
-from utils.github import get_handler, get_private_key
-from petercat_utils import get_env_variable
-
-import jwt
-from cryptography.hazmat.primitives import serialization
-from cryptography.hazmat.backends import default_backend
-
-APP_ID = get_env_variable("X_GITHUB_APP_ID")
-WEB_URL = get_env_variable("WEB_URL")
-
-logger = logging.getLogger()
-logger.setLevel("INFO")
-
-router = APIRouter(
- prefix="/api/github",
- tags=["github"],
- responses={404: {"description": "Not found"}},
-)
-
-def get_jwt():
- payload = {
- # Issued at time
- 'iat': int(time.time()),
- # JWT expiration time (10 minutes maximum)
- 'exp': int(time.time()) + 600,
- # GitHub App's identifier
- 'iss': APP_ID
- }
-
- pem = get_private_key()
- private_key = serialization.load_pem_private_key(
- pem.encode("utf-8"), password=None, backend=default_backend()
- )
- return jwt.encode(payload, private_key, algorithm='RS256')
-
-def get_app_installations_access_token(installation_id: str, jwt: str):
- url = f"https://api.github.com/app/installations/{installation_id}/access_tokens"
- print("get_app_installations_access_token", url, jwt)
- resp = requests.post(url,
- headers={
- 'X-GitHub-Api-Version': '2022-11-28',
- 'Accept': 'application/vnd.github+json',
- 'Authorization': f"Bearer {jwt}"
- }
- )
-
- return resp.json()
-
-def get_installation_repositories(access_token: str):
- url = "https://api.github.com/installation/repositories"
- print("get_installation_repositories", url)
- resp = requests.get(url, headers={
- 'X-GitHub-Api-Version': '2022-11-28',
- 'Accept': 'application/vnd.github+json',
- 'Authorization': f"Bearer {access_token}"
- })
- return resp.json()
-
-
-# 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":
- return { "success": False, "message": f"Invalid setup_action value {setup_action}" }
- elif authorization_dao.exists(installation_id=installation_id):
- return { "success": False, "message": f"Installation_id {installation_id} Exists" }
- 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)
- print(f"github_app_callback: success={success}, message={message}")
-
- installed_repositories = get_installation_repositories(access_token=access_token['token'])
- for repo in installed_repositories["repositories"]:
- repository_config = RepositoryConfig(repo_name=repo["full_name"], 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)
-
-@router.post("/app/webhook")
-async def github_app_webhook(
- request: Request,
- background_tasks: BackgroundTasks,
- x_github_event: str = Header(...),
-):
- payload = await request.json()
- if "installation" not in payload:
- return {"success": False, "message": "Invalid Webhook request"}
-
- installation_id = payload["installation"]["id"]
- try:
- auth = Auth.AppAuth(
- app_id=APP_ID, private_key=get_private_key(), jwt_algorithm="RS256"
- ).get_installation_auth(installation_id=int(installation_id))
- except Exception as e:
- print("Failed", f"Authentication failed: {e}")
- return {"success": False, "message": f"Authentication failed: {e}"}
-
- handler = get_handler(
- x_github_event, payload, auth, installation_id=installation_id
- )
- if handler:
- await handler.execute()
- return {"success": True}
- else:
- print("Failed, Unsupported GitHub event")
- return {"success": False, "message": "Unsupported GitHub event"}
-
-@router.get("/user/organizations")
-async def get_user_organizations(user_access_token: Annotated[str | None, Depends(get_user_access_token)] = None):
- if user_access_token is None:
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Github Login needed")
- auth = Auth.Token(token=user_access_token)
- g = Github(auth=auth)
- user = g.get_user()
- orgs = user.get_orgs()
-
- return [org.raw_data for org in orgs]
-
-@router.get("/orgs/{org_id}/repos")
-async def get_org_repos(org_id: str, user_access_token: Annotated[str | None, Depends(get_user_access_token)] = None):
- if user_access_token is None:
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Github Login needed")
- auth = Auth.Token(token=user_access_token)
- g = Github(auth=auth)
- org = g.get_organization(org_id)
- repos = org.get_repos()
- print(f"repos={repos}")
- return [repo.raw_data for repo in repos]
diff --git a/server/routers/health_checker.py b/server/routers/health_checker.py
deleted file mode 100644
index cfd90517..00000000
--- a/server/routers/health_checker.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from fastapi import APIRouter
-
-router = APIRouter(
- prefix="/api",
- tags=["health_checkers"],
- responses={404: {"description": "Not found"}},
-)
-
-@router.get("/health_checker")
-def health_checker():
- return { "Hello": "World" }
diff --git a/server/sql/rag_docs.sql b/server/sql/rag_docs.sql
deleted file mode 100644
index 8bb667ef..00000000
--- a/server/sql/rag_docs.sql
+++ /dev/null
@@ -1,104 +0,0 @@
--- Supabase AI is experimental and may produce incorrect answers
--- Always verify the output before executing
-
--- Enable the pgvector extension to work with embedding vectors
-create extension
-if not exists vector;
-
--- Create a table to store your rag_docs
-create table rag_docs
-(
- id uuid primary key,
- content text,
- -- corresponds to Document.pageContent
- metadata jsonb,
- -- corresponds to Document.metadata
- embedding vector (1536),
- -- 1536 works for OpenAI embeddings, change if needed
- -- per request info
- repo_name varchar,
- commit_id varchar,
- bot_id varchar,
- file_sha varchar,
- file_path varchar
-);
-
-create table rag_issues
-(
- id uuid primary key,
- content text,
- -- corresponds to Document.pageContent
- metadata jsonb,
- -- corresponds to Document.metadata
- embedding vector (1536),
- -- 1536 works for OpenAI embeddings, change if needed
- -- per request info
- repo_name varchar,
- issue_id varchar,
- bot_id varchar
-);
-
--- Drop the existing function if it already exists
-drop function if exists match_rag_docs;
-
-create function match_rag_docs
- (
- query_embedding vector (1536),
- filter jsonb default '{}'
-) returns table
-(
- id uuid,
- content text,
- metadata jsonb,
- embedding vector,
- similarity float
-) language plpgsql as $$
-#variable_conflict use_column
-begin
- return query
- select
- id,
- content,
- metadata,
- embedding,
- 1 - (rag_docs.embedding <=> query_embedding
- ) as similarity
- from rag_docs
- where metadata @> jsonb_extract_path(filter, 'metadata')
- and bot_id = jsonb_extract_path_text(filter, 'bot_id')
- order by rag_docs.embedding <=> query_embedding;
-end;
-$$;
-
-
--- Drop the existing function if it already exists
-drop function if exists match_rag_issues;
-
-create function match_rag_issues
- (
- query_embedding vector (1536),
- filter jsonb default '{}'
-) returns table
-(
- id uuid,
- content text,
- metadata jsonb,
- embedding vector,
- similarity float
-) language plpgsql as $$
-#variable_conflict use_column
-begin
- return query
- select
- id,
- content,
- metadata,
- embedding,
- 1 - (rag_issues.embedding <=> query_embedding
- ) as similarity
- from rag_issues
- where metadata @> jsonb_extract_path(filter, 'metadata')
- and bot_id = jsonb_extract_path_text(filter, 'bot_id')
- order by rag_issues.embedding <=> query_embedding;
-end;
-$$;
diff --git a/server/routers/task.py b/server/task/router.py
similarity index 100%
rename from server/routers/task.py
rename to server/task/router.py