From c1768114de381e37e272e9faf7db8e95a93ff381 Mon Sep 17 00:00:00 2001 From: zhaoqingang <zhaoqg0118@163.com> Date: 星期一, 11 十一月 2024 16:37:12 +0800 Subject: [PATCH] role group user.. --- app/api/role.py | 66 +++ app/models/agent_model.py | 112 +++++ app/models/organization_model.py | 111 +++++ app/models/llm_model.py | 33 + app/models/resource_model.py | 140 +++++++ app/service/organization.py | 0 app/Log/log.log | 1 app/service/ragflow.py | 5 app/service/group.py | 68 +- app/service/user.py | 11 app/api/group.py | 32 app/api/user.py | 31 - app/models/__init__.py | 9 app/models/role_model.py | 83 ++++ app/models/user_model.py | 108 +++++ app/api/organization.py | 0 app/models/knowledge_model.py | 62 +++ app/Log/__init__.py | 16 main.py | 6 app/models/group_model.py | 119 +++++- app/service/role.py | 65 +++ app/models/dialog_model.py | 62 +++ 22 files changed, 1,036 insertions(+), 104 deletions(-) diff --git a/app/Log/__init__.py b/app/Log/__init__.py new file mode 100644 index 0000000..ebc086b --- /dev/null +++ b/app/Log/__init__.py @@ -0,0 +1,16 @@ +import logging +import logging.handlers + +def init_log(): + logger = logging.getLogger() + logger.setLevel(logging.INFO) + # ch = logging.StreamHandler() + fh = logging.handlers.RotatingFileHandler("app/Log/log.log",mode="a",maxBytes = 100*1024, backupCount = 10) + formatter = logging.Formatter( + "%(asctime)s - %(module)s - %(funcName)s - line:%(lineno)d - %(levelname)s - %(message)s" + ) + fh.setFormatter(formatter) + logger.addHandler(fh) # 灏嗘棩蹇楄緭鍑鸿嚦鏂囦欢 + return logger + +logger = init_log() \ No newline at end of file diff --git a/app/Log/log.log b/app/Log/log.log new file mode 100644 index 0000000..09ff359 --- /dev/null +++ b/app/Log/log.log @@ -0,0 +1 @@ +2024-11-11 16:33:10,255 - user - get_user_list - line:5 - ERROR - ffffffff diff --git a/app/api/group.py b/app/api/group.py index 4485d31..4d0826e 100644 --- a/app/api/group.py +++ b/app/api/group.py @@ -1,16 +1,13 @@ -from ast import parse - from fastapi import APIRouter, Depends -from app.api import Response, pwd_context, get_current_user, ResponseList -from app.config.config import settings +from win32ctypes.pywin32.pywintypes import datetime + +from app.api import Response, get_current_user from app.models.base_model import get_db -from app.models.group_model import GroupInfoModel, UserGroupModel, GroupData, GroupUsers +from app.models.group_model import GroupModel, GroupData, GroupUsers from app.models.user import PageParameter from app.models.user_model import UserModel -from app.service.bisheng import BishengService from app.service.group import create_group, group_list, edit_group_data, delete_group_data, get_group_users, \ save_user_to_group -from app.service.token import get_bisheng_token group_router = APIRouter() @@ -25,7 +22,7 @@ async def add_group(group: GroupData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): if not group.group_name: return Response(code=400, msg="The group_name cannot be empty!") - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_name == group.group_name).first() + db_group = db.query(GroupModel).filter(GroupModel.group_name == group.group_name).first() if db_group: return Response(code=200, msg="group already created") is_create = await create_group(db, group.group_name, group.group_description) @@ -38,11 +35,12 @@ async def edit_group(group: GroupData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): if not group.group_name: return Response(code=400, msg="The group_name cannot be empty!") - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_name == group.group_name).first() + db_group = db.query(GroupModel).filter(GroupModel.group_name == group.group_name).first() if db_group: return Response(code=200, msg="group_name already created") is_edit = await edit_group_data(db, group.id, - {"group_name": group.group_name, "group_description": group.group_description}) + {"group_name": group.group_name, "group_description": group.group_description, + "updated_at": datetime.now()}) if not is_edit: return Response(code=200, msg="group edit failure", data={}) return Response(code=200, msg="group edit successfully", data={"group_name": group.group_name}) @@ -50,13 +48,13 @@ @group_router.post("/edit_group_status", response_model=Response) async def edit_group_status(group: GroupData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): - if group.group_status not in [0, 1]: + if group.group_status not in ["0", "1"]: return Response(code=400, msg="The status cannot be {}!".format(group.group_status)) - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group.id).first() + db_group = db.query(GroupModel).filter(GroupModel.id == group.id).first() if not db_group: return Response(code=200, msg="group does not exist") is_edit = await edit_group_data(db, group.id, - {"group_status": group.group_status}) + {"status": group.group_status,"updated_at": datetime.now()}) if not is_edit: return Response(code=200, msg="group status edit failure", data={}) return Response(code=200, msg="group status edit successfully", data={"group_name": group.group_name}) @@ -64,7 +62,7 @@ @group_router.post("/delete_group", response_model=Response) async def delete_group(group: GroupData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group.id).first() + db_group = db.query(GroupModel).filter(GroupModel.id == group.id).first() if not db_group: return Response(code=200, msg="group does not exist") is_edit = await delete_group_data(db, group.id) @@ -75,16 +73,16 @@ @group_router.post("/group_users", response_model=Response) async def group_users(group: GroupData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group.id).first() + db_group = db.query(GroupModel).filter(GroupModel.id == group.id).first() if not db_group: - return Response(code=200, data={}) + return Response(code=200, msg="group does not exist", data={}) return Response(code=200, msg="success", data=await get_group_users(db, group.id)) @group_router.post("/save_group_user", response_model=Response) async def save_group_user(group_user: GroupUsers, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): - db_group = db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group_user.id).first() + db_group = db.query(GroupModel).filter(GroupModel.id == group_user.id).first() if not db_group: return Response(code=200, msg="group does not exist") is_success = await save_user_to_group(db, current_user.id, group_user.id, group_user.user_list) diff --git a/app/api/organization.py b/app/api/organization.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/api/organization.py diff --git a/app/api/role.py b/app/api/role.py new file mode 100644 index 0000000..dfcc7bb --- /dev/null +++ b/app/api/role.py @@ -0,0 +1,66 @@ +# coding:utf-8 + +from fastapi import APIRouter, Depends +from app.api import Response, get_current_user +from app.models.base_model import get_db +from app.models.role_model import RoleData, RoleModel, RoleStatus, RoleEdit +from app.models.user import PageParameter +from app.models.user_model import UserModel +from app.service.role import role_list, create_role, delete_role_data, edit_role_status, edit_role_resource + +role_router = APIRouter() + +@role_router.get("/list", response_model=Response) +async def user_group_list(paras: PageParameter, current_user: UserModel = Depends(get_current_user), + db=Depends(get_db)): + return Response(code=200, msg="", data=await role_list(db, paras.page_size, paras.page_index, paras.keyword)) + + +@role_router.post("/add_role", response_model=Response) +async def add_role(role: RoleData, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): + if not role.roleName: + return Response(code=400, msg="The roleName cannot be empty!") + db_role = db.query(RoleModel).filter(RoleModel.name == role.roleName).first() + if db_role: + return Response(code=200, msg="role already created") + is_create = await create_role(db, role.roleName, role.remark, current_user.id) + if not is_create: + return Response(code=200, msg="role create failure", data={}) + return Response(code=200, msg="role create successfully", data={"roleName": role.roleName}) + +@role_router.delete("/delete_role/{role_id}", response_model=Response) +async def delete_group(role_id: str, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): + db_role = db.query(RoleModel).filter(RoleModel.id == role_id).first() + if not db_role: + return Response(code=200, msg="role does not exist") + is_edit = await delete_role_data(db, role_id) + if not is_edit: + return Response(code=200, msg="role delete failure", data={}) + return Response(code=200, msg="role delete successfully", data={}) + + +@role_router.put("/change_status", response_model=Response) +async def edit_group_status(role: RoleStatus, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): + if role.status not in ["0", "1"]: + return Response(code=400, msg="The status cannot be {}!".format(role.status)) + db_role = db.query(RoleModel).filter(RoleModel.id == role.role_id).first() + if not db_role: + return Response(code=200, msg="role does not exist") + is_edit = await edit_role_status(db, role.status,role.role_id) + if not is_edit: + return Response(code=200, msg="role status edit failure", data={}) + return Response(code=200, msg="role status edit successfully", data={}) + +@role_router.put("/edit_role", response_model=Response) +async def edit_role_data(role: RoleEdit, current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): + db_role = db.query(RoleModel).filter(RoleModel.id == role.role_id).first() + if not db_role: + return Response(code=200, msg="role does not exist") + if role.roleName: + db_role = db.query(RoleModel).filter(RoleModel.name == role.roleName).first() + if db_role: + return Response(code=200, msg="role already created") + is_edit = await edit_role_resource(db, role.role_id,role.roleName, role.remark, role.resources) + if not is_edit: + return Response(code=200, msg="role edit failure", data={}) + return Response(code=200, msg="role edit successfully", data={}) \ No newline at end of file diff --git a/app/api/user.py b/app/api/user.py index ca725f4..6f98233 100644 --- a/app/api/user.py +++ b/app/api/user.py @@ -1,35 +1,16 @@ from fastapi import APIRouter, Depends -from app.api import Response, pwd_context, get_current_user, ResponseList -from app.config.config import settings +from app.api import Response, pwd_context, get_current_user from app.models.base_model import get_db -from app.models.group_model import UserGroupModel +from app.models.user import PageParameter from app.models.user_model import UserModel -from app.service.bisheng import BishengService -from app.service.ragflow import RagflowService -from app.service.token import get_bisheng_token +from app.service.user import get_user_list user_router = APIRouter() -@user_router.post("/list", response_model=Response) -async def user_list(current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): - - bisheng_service = BishengService(settings.sgb_base_url) - ragflow_service = RagflowService(settings.fwr_base_url) - db_user = db.query(UserModel).filter(UserGroupModel.group_name == UserModel.username).first() - if db_user: - return Response(code=200, msg="Username already registered") - # 娉ㄥ唽鍒版瘯鏄� - try: - token = get_bisheng_token(db, current_user.id) - print(token) - result = await bisheng_service.user_list(token) - print(result) - except Exception as e: - return Response(code=500, msg=f"Failed to register with Bisheng: {str(e)}") - - - return ResponseList(code=200, msg="", data=result) +@user_router.get("/list", response_model=Response) +async def user_list(paras: PageParameter,current_user: UserModel = Depends(get_current_user), db=Depends(get_db)): + return Response(code=200, msg="", data=await get_user_list(db, paras.page_size, paras.page_index, paras.keyword)) diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..1ec93e6 --- /dev/null +++ b/app/models/__init__.py @@ -0,0 +1,9 @@ +from .agent_model import * +from .dialog_model import * +from .group_model import * +from .knowledge_model import * +from .llm_model import * +from .organization_model import * +from .resource_model import * +from .role_model import * +from .user_model import * \ No newline at end of file diff --git a/app/models/agent_model.py b/app/models/agent_model.py index f247db9..1b1cb00 100644 --- a/app/models/agent_model.py +++ b/app/models/agent_model.py @@ -1,5 +1,7 @@ +import json +from datetime import datetime from enum import IntEnum -from sqlalchemy import Column, String, Enum as SQLAlchemyEnum, Integer +from sqlalchemy import Column, String, Enum as SQLAlchemyEnum, Integer, BigInteger, DateTime, Text, Float, Boolean from app.models.base_model import Base @@ -25,3 +27,111 @@ 'agent_type': self.agent_type, 'type': self.type } + + +class CanvasModel(Base): + __tablename__ = 'user_canvas' + __mapper_args__ = { + # "order_by": 'SEQ' + } + id = Column(String(32), primary_key=True) + create_time = Column(BigInteger) + create_date = Column(DateTime, default=datetime.now) + update_time = Column(BigInteger) + update_date = Column(DateTime, default=datetime.now) + avatar = Column(Text) + user_id = Column(String(255)) + title = Column(String(255)) + description = Column(Text) + canvas_type = Column(String(32)) + dsl = Column(Text) + + + + def get_id(self): + return str(self.id) + + def to_json(self): + return { + 'id': self.id, + 'create_time': self.create_time, + 'create_date': self.create_date, + 'update_time': self.update_time, + 'update_date': self.update_date, + 'avatar': self.avatar, + 'user_id': self.user_id, + 'title': self.title, + 'description': self.description, + 'canvas_type': self.canvas_type, + 'dsl': self.dsl + } + + +class UnifiedAgentModel(Base): + __tablename__ = 'unified_agent' + + id = Column(String(32), primary_key=True) + tenant_id = Column(String(32)) + name = Column(String(255)) + description = Column(Text) + icon = Column(Text) + prompt_type = Column(String(16)) + prompt_config = Column(Text, default='{}') + status = Column(String(1)) + prompts = Column(Text, default='[]') + language = Column(String(32)) + llm_id = Column(String(128), default='') + llm_setting = Column(Text, default='{}') + similarity_threshold = Column(Float, default=0.0) + vector_similarity_weight = Column(Float, default=0.0) + top_n = Column(Integer, default=0) + top_k = Column(Integer, default=0) + do_refer = Column(String(1), default='') + rerank_id = Column(String(128), default='') + kb_ids = Column(Text, default='[]') + hide = Column(Boolean, default=False) + type = Column(Integer) + canvas_type = Column(String(32), default="") + dsl = Column(Text, default='{}') + + def get_id(self): + return str(self.id) + + def to_json(self): + if self.prompts is None or self.prompts == '': + self.prompts = '[]' + if self.prompt_config is None or self.prompt_config == '': + self.prompt_config = '{}' + if self.dsl is None or self.dsl == '': + self.dsl = '{}' + if self.kb_ids is None or self.kb_ids == '': + self.kb_ids = '[]' + return { + 'id': self.id, + 'create_time': self.create_time, + 'create_date': self.create_date, + 'update_time': self.update_time, + 'update_date': self.update_date, + 'tenant_id': self.tenant_id, + 'name': self.name, + 'description': self.description, + 'icon': self.icon, + 'language': self.language, + 'llm_id': self.llm_id, + 'similarity_threshold': self.similarity_threshold, + 'vector_similarity_weight': self.vector_similarity_weight, + 'top_n': self.top_n, + 'top_k': self.top_k, + 'do_refer': self.do_refer, + 'kb_ids': self.kb_ids, + 'status': self.status, + 'prompt_type': self.prompt_type, + 'prompt_config': json.loads(self.prompt_config), + 'prompts': json.loads(self.prompts), + 'dsl': json.loads(self.dsl) + } + + @staticmethod + def is_type(record_id, t): + record = UnifiedAgent.get_by_id(record_id) + return record and record.type == t \ No newline at end of file diff --git a/app/models/dialog_model.py b/app/models/dialog_model.py new file mode 100644 index 0000000..bfa4a8c --- /dev/null +++ b/app/models/dialog_model.py @@ -0,0 +1,62 @@ +from datetime import datetime + +from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime, BigInteger, Text, Float, Boolean +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +class DialogModel(Base): + __tablename__ = 'dialogs' + __mapper_args__ = { + # "order_by": 'SEQ' + } + id = Column(String(32), primary_key=True) + create_time = Column(BigInteger) + create_date = Column(DateTime) + update_time = Column(BigInteger) + update_date = Column(DateTime) + tenant_id = Column(String(32)) + name = Column(String(255)) + description = Column(Text) + icon = Column(Text) + language = Column(String(32)) + llm_id = Column(String(128)) + llm_setting = Column(Text) + prompt_type = Column(String(16)) + prompt_config = Column(Text) + similarity_threshold = Column(Float) + vector_similarity_weight = Column(Float) + top_n = Column(Integer) + top_k = Column(Integer) + do_refer = Column(String(1)) + rerank_id = Column(String(128)) + kb_ids = Column(Text) + status = Column(String(1)) + hide = Column(Boolean) + + def get_id(self): + return str(self.id) + + def to_json(self): + return { + 'id': self.id, + 'create_time': self.create_time, + 'create_date': self.create_date, + 'update_time': self.update_time, + 'update_date': self.update_date, + 'tenant_id': self.tenant_id, + 'name': self.name, + 'description': self.description, + 'icon': self.icon, + 'language': self.language, + 'llm_id': self.llm_id, + 'prompt_type': self.prompt_type, + 'prompt_config': self.prompt_config, + 'similarity_threshold': self.similarity_threshold, + 'vector_similarity_weight': self.vector_similarity_weight, + 'top_n': self.top_n, + 'top_k': self.top_k, + 'do_refer': self.do_refer, + 'kb_ids': self.kb_ids, + 'status': self.status + } \ No newline at end of file diff --git a/app/models/group_model.py b/app/models/group_model.py index ec03d7d..52e05a8 100644 --- a/app/models/group_model.py +++ b/app/models/group_model.py @@ -2,49 +2,128 @@ from enum import IntEnum from typing import Optional -from sqlalchemy import Column, Integer, String, DateTime, Enum, Index +from sqlalchemy import Column, Integer, String, DateTime, Enum, Index, Table, ForeignKey from pydantic import BaseModel +from sqlalchemy.orm import relationship, backref + from app.models.base_model import Base -class GroupStatus(IntEnum): - NO = 1 - OFF = 0 + +group_knowledge_table = Table('group_Knowledge', Base.metadata + , Column('group_id', Integer, ForeignKey('group.id')) + , Column('knowledge_id', String(32), ForeignKey('knowledgebase.id'))) + +group_dialog_table = Table('group_dialogs', Base.metadata + , Column('group_id', Integer, ForeignKey('group.id')) + , Column('dialog_id', String(36), ForeignKey('dialogs.id'))) + +group_agent_table = Table('group_agent', Base.metadata + , Column('group_id', Integer, ForeignKey('group.id')) + , Column('agent_id', String(36), ForeignKey('user_canvas.id'))) + +group_llm_table = Table('group_llm', Base.metadata + , Column('group_id', Integer, ForeignKey('group.id')) + , Column('llm_id', String(36), ForeignKey('common_llm.id'))) + +group_unified_agent_table = Table('group_unified_agent', Base.metadata + , Column('group_id', Integer, ForeignKey('group.id')) + ,Column('unified_agent_id', String(36), ForeignKey('unified_agent.id'))) - -class GroupInfoModel(Base): - __tablename__ = "group_info" - group_id = Column(Integer, primary_key=True, index=True) +class GroupModel(Base): + __tablename__ = "group" + id = Column(Integer, primary_key=True, index=True) group_name = Column(String(255), unique=True, nullable=False, index=True) group_description = Column(String(255)) - group_status = Column(Integer, nullable=False, default=1) + status = Column(String(10), nullable=False, default="1") created_at = Column(DateTime, default=datetime.now()) updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + knowledges = relationship('KnowledgeModel', + secondary=group_knowledge_table, + backref=backref('groups', lazy='dynamic'), + lazy="dynamic") + + dialogs = relationship('DialogModel', + secondary=group_dialog_table, + backref=backref('groups', lazy='dynamic'), + lazy="dynamic") + + agents = relationship('CanvasModel', + secondary=group_agent_table, + backref=backref('groups', lazy='dynamic'), + lazy="dynamic") + + llms = relationship('CommonLlmModel', + secondary=group_llm_table, + backref=backref('groups', lazy='dynamic'), + lazy="dynamic") + + unified_agents = relationship('UnifiedAgentModel', + secondary=group_unified_agent_table, + backref=backref('roles', lazy='dynamic'), + lazy="dynamic") def to_dict(self): return { - 'id': self.group_id, + 'groupId': self.id, 'name': self.group_name, - 'group_description': self.group_description, - 'group_status': self.group_status, - 'created_at': self.created_at.strftime("%Y.%m.%d %H:%M") + 'description': self.group_description, + 'status': self.status, + 'createTime': self.created_at.strftime("%Y-%m-%d %H:%M:%S") } -class UserGroupModel(Base): - __tablename__ = "user_group" - id = Column(Integer, primary_key=True) - group_id = Column(Integer, nullable=False) - user_id = Column(Integer, nullable=False) - Index('ix_user_group_id', group_id, user_id, unique=True) + def get_id(self): + return str(self.ID) + + + def __repr__(self): + return '<Role name:%r description:%r iconCls:%r seq:%r>\n' \ + % (self.NAME, self.DESCRIPTION, self.ICONCLS, self.SEQ) + + def to_json(self): + json = { + 'roleId': self.id, + 'createTime': self.created_at.strftime('%Y-%m-%d %H:%M:%S'), + 'updateTime': self.updated_at.strftime('%Y-%m-%d %H:%M:%S'), + 'groupName': self.group_name, + 'remark': self.group_description, + 'status': self.status, + } + if len(self.knowledges.all()) > 0: + json['knowledges'] = [knowledge.to_json() for knowledge in self.knowledges] + + if len(self.dialogs.all()) > 0: + json['dialogs'] = [dialog.to_json() for dialog in self.dialogs] + + if len(self.agents.all()) > 0: + json['agents'] = [agent.to_json() for agent in self.agents] + + if len(self.llms.all()) > 0: + json['llms'] = [llm.to_json() for llm in self.llms] + + json['users'] = [user.to_dict() for user in self.users] + + if hasattr(self, 'flag'): + json['flag'] = self.flag + + return json + + +# class UserGroupModel(Base): +# __tablename__ = "user_group" +# id = Column(Integer, primary_key=True) +# group_id = Column(Integer, nullable=False) +# user_id = Column(Integer, nullable=False) +# Index('ix_user_group_id', group_id, user_id, unique=True) class GroupData(BaseModel): id: Optional[int] = None group_name: Optional[str] = "" group_description: Optional[str] = "" - group_status: Optional[int] = None + group_status: Optional[str] = "" class GroupUsers(BaseModel): id: int diff --git a/app/models/knowledge_model.py b/app/models/knowledge_model.py new file mode 100644 index 0000000..cc8d1b8 --- /dev/null +++ b/app/models/knowledge_model.py @@ -0,0 +1,62 @@ +from datetime import datetime + +from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime, BigInteger, Text, Float, Boolean +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +class KnowledgeModel(Base): + __tablename__ = 'knowledgebase' + __mapper_args__ = { + # "order_by": 'SEQ' + } + id = Column(String(32), primary_key=True) + name = Column(String(128)) + create_time = Column(BigInteger) + create_date = Column(DateTime) + update_time = Column(BigInteger) + update_date = Column(DateTime) + avatar = Column(Text) + tenant_id = Column(String(32)) + language = Column(String(32)) + description = Column(Text) + embd_id = Column(String(128)) + permission = Column(String(16)) + created_by = Column(String(32)) + doc_num = Column(Integer) + token_num = Column(Integer) + chunk_num = Column(Integer) + similarity_threshold = Column(Float) + vector_similarity_weight = Column(Float) + parser_id = Column(String(32)) + parser_config = Column(Text) + status = Column(String(1)) + hide = Column(Boolean) + + def get_id(self): + return str(self.id) + + def to_json(self): + return { + 'id': self.id, + 'name': self.name, + 'create_time': self.create_time, + 'update_time': self.update_time, + 'avatar': self.avatar, + 'tenant_id': self.tenant_id, + 'language': self.language, + 'description': self.description, + 'embd_id': self.embd_id, + 'permission': self.permission, + 'created_by': self.created_by, + 'doc_num': self.doc_num, + 'token_num': self.token_num, + 'chunk_num': self.chunk_num, + 'similarity_threshold': self.similarity_threshold, + 'vector_similarity_weight': self.vector_similarity_weight, + 'parser_id': self.parser_id, + 'parser_config': json.loads(self.parser_config), + 'status': self.status + } + def __repr__(self): + return '<Knowledge name:%r url:%r>\n' % (self.name, self.id) \ No newline at end of file diff --git a/app/models/llm_model.py b/app/models/llm_model.py new file mode 100644 index 0000000..d80b419 --- /dev/null +++ b/app/models/llm_model.py @@ -0,0 +1,33 @@ +from datetime import datetime +from enum import IntEnum +from typing import Optional + +from sqlalchemy import Column, Integer, String, DateTime, Enum, Index, Table, ForeignKey +from pydantic import BaseModel +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +class CommonLlmModel(Base): + __tablename__ = 'common_llm' + __mapper_args__ = { + # "order_by": 'SEQ' + } + + id = Column(String(36), primary_key=True, nullable=False) + llm_factory = Column(String(128), nullable=False) + model_type = Column(String(128), nullable=False) + llm_name = Column(String(128), nullable=False) + api_key = Column(String(1024), nullable=True) + api_base = Column(String(255), nullable=True) + + def to_json(self): + json = { + 'id': self.ID, + 'llm_factory': self.llm_factory, + 'model_type': self.model_type, + 'llm_name': self.llm_name, + 'api_key': self.api_key, + 'api_base': self.api_base, + } + return json \ No newline at end of file diff --git a/app/models/organization_model.py b/app/models/organization_model.py new file mode 100644 index 0000000..ae7a1de --- /dev/null +++ b/app/models/organization_model.py @@ -0,0 +1,111 @@ +from datetime import datetime + +from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +organization_group_table = Table('organization_group', Base.metadata, + Column('group_id', Integer, ForeignKey('group.id')), + Column('organization_id', String(36), ForeignKey('organization.id'))) + +# #鏋勫缓瀵箁ole琛ㄧ殑鍏崇郴 +organization_role_table = Table('organization_role', Base.metadata, + Column('role_id', String(36), ForeignKey('role.id')), + Column('organization_id', String(36), ForeignKey('organization.id'))) + +class OrganizationModel(Base): + __tablename__ = 'organization' + id = Column(String(36), primary_key=True) + created_at = Column(DateTime, default=datetime.now()) + updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + name = Column(String(200)) + address = Column(String(200)) + code = Column(String(200)) + iconcls = Column(String(255)) + seq = Column(Integer) + leader = Column(String(255)) + phone = Column(String(11)) + email = Column(String(64)) + status = Column(String(10), nullable=False, default="0") + + groups = relationship('GroupModel', + secondary=organization_group_table, + backref=backref('organizations', lazy='dynamic')) + + roles = relationship('RoleModel', + secondary=organization_role_table, + backref=backref('organizations', lazy='dynamic')) + + + organization_id = Column(String(36), ForeignKey('organization.id')) + + parent = relationship('OrganizationModel', remote_side=[id], backref='childrens', uselist=False) + + children = relationship('OrganizationModel') + + def to_json(self): + json = { + 'deptId': self.id, + 'createTime': self.created_at, + 'updateTime': self.updated_at, + 'deptName': self.name, + 'address': self.address, + 'code': self.code, + 'iconCls': self.iconcls, + 'orderNum': self.seq, + 'parentId': self.get_pid(), + 'parentName': self.get_pName(), + 'leader': self.leader, + 'phone': self.phone, + 'email': self.email, + 'status': self.status, + 'roles': [self.role_json(role) for role in self.roles], + 'groups': [self.group_json(group) for group in self.groups], + 'children': [ + org.to_json() for org in self.children + ] + } + + + + return json + + def role_json(self, role): + return { + 'roleId': role.id, + 'roleName': role.name, + 'deptId': self.id, + } + + def group_json(self, group): + return { + 'groupId': group.id, + 'groupName': group.name, + 'deptId': self.id, + } + + def to_tree_select_json(self): + return { + 'id': self.ID, + 'label': self.NAME, + 'children': [org.to_tree_select_json() for org in self.children] + } + + def get_pid(self): + if self.parent: + return self.parent.ID + return '' + + def get_pName(self): + if self.parent: + return self.parent.NAME + return '' + + + + def get_id(self): + return str(self.ID) + + def __repr__(self): + return '<Organization %r>\n' %(self.NAME) \ No newline at end of file diff --git a/app/models/resource_model.py b/app/models/resource_model.py new file mode 100644 index 0000000..3aeff5a --- /dev/null +++ b/app/models/resource_model.py @@ -0,0 +1,140 @@ +from datetime import datetime +from sqlalchemy import Column, Integer, String, DateTime, Table, ForeignKey +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +class ResourceModel(Base): + __tablename__ = 'resource' + id = Column(String(36), primary_key=True, index=True) + created_at = Column(DateTime, default=datetime.now()) + updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + name = Column(String(128)) + url = Column(String(200)) + path = Column(String(200)) + perms = Column(String(150)) + description = Column(String(200)) + icon = Column(String(100)) + seq = Column(Integer) + target = Column(String(100)) + canbdeeleted = Column(Integer) + resource_type_id = Column(String(36), ForeignKey('resource_type.id')) + resource_id = Column(String(36), ForeignKey('resource.id')) + parent = relationship('ResourceModel', remote_side=[id], backref='resources', uselist=False) + children = relationship('ResourceModel') + status = Column(String(10)) + hidden = Column(Integer, default=0) + def get_id(self): + return str(self.ID) + + def to_json(self): + return { + 'menuId': self.id, + 'createTime': self.created_at, + 'updateTime': self.updated_at, + 'menuName': self.name, + 'component': self.url, + 'description': self.description, + 'icon': self.icon, + 'orderNum': self.seq, + 'target': self.target, + 'parentId': self.get_pid(), + 'parentName': self.get_pName(), + 'syresourcetype': self.get_type_json(), + 'status': self.status, + 'path': self.path, + 'perms': self.perms, + # 绫诲瀷锛�0鐩綍 1鑿滃崟 2鎸夐挳锛� + 'menuType': self.resource_type_id, + } + + def to_tree_select_json(self): + return { + 'menuId': self.id, + 'menuName': self.name, + 'menuType': self.resource_type_id, + 'children': [res.to_tree_select_json() for res in self.children] + } + + def to_router_json(self): + router = { + 'name': self.path.capitalize() if self.path else '', + 'path': self.path, + 'hidden': bool(self.hidden), + 'redirect': 'noRedirect', + 'component': self.url, + 'alwaysShow': True, + 'meta': { + 'title': self.name, + 'icon': self.icon, + 'noCache': False, + 'link': '' + }, + 'children': [ + res.to_router_json() for res in self.children if res.type.id == '3' or res.type.id == '0' + ] + } + + if not router['children']: + del router['children'] + del router['redirect'] + del router['alwaysShow'] + if not router['component']: + router['component'] = 'Layout' + + return router + + def to_menu_json(self): + return { + 'id': self.id, + 'iconCls': self.icon, + 'pid': self.get_pid(), + 'state': 'open', + 'checked': False, + 'attributes': { + 'target': self.target, + 'url': self.url + }, + 'text': self.name + } + + def get_pid(self): + if self.parent: + return self.parent.id + return '' + + def get_pName(self): + if self.parent: + return self.parent.name + return '' + + def get_type_json(self): + if self.type: + return self.type.to_json() + return {} + + def __repr__(self): + return '<ResourceModel name:%r url:%r>\n' % (self.NAME, self.URL) + + +class ResourceTypeModel(Base): + __tablename__ = 'resource_type' + id = Column(String(36), primary_key=True) + created_at = Column(DateTime, default=datetime.now()) + updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + name = Column(String(128)) + description = Column(String(255)) + + resources = relationship('ResourceModel', backref='type', lazy='dynamic') + + def to_json(self): + return { + 'id': self.id, + 'createdatetime': self.created_at, + 'updatedatetime': self.updated_at, + 'name': self.name, + 'description': self.description + } + + def __repr__(self): + return '<ResourceTypeModel %r>\n' %(self.name) \ No newline at end of file diff --git a/app/models/role_model.py b/app/models/role_model.py new file mode 100644 index 0000000..3121054 --- /dev/null +++ b/app/models/role_model.py @@ -0,0 +1,83 @@ +from datetime import datetime +from typing import Optional + +from pydantic import BaseModel, constr +from sqlalchemy import Column, Integer, String, DateTime, Table, ForeignKey +from sqlalchemy.orm import relationship, backref + +from app.models.base_model import Base + +# 瑙掕壊璧勬簮鍏宠仈琛� +role_resource_table = Table('role_resource', Base.metadata, + Column('role_id', String(36), ForeignKey('role.id')), + Column('resource_id', String(36), ForeignKey('resource.id'))) + + + +class RoleModel(Base): + __tablename__ = 'role' + id = Column(String(36), primary_key=True, index=True) + created_at = Column(DateTime, default=datetime.now()) + updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + name = Column(String(128), unique=True, nullable=False, index=True) + description = Column(String(255)) + iconCls = Column(String(100)) + seq = Column(Integer) + roleKey = Column(String(100)) + dataScope = Column(Integer) + status = Column(String(10), default="0") + creator = Column(Integer) + + # 鍖呭惈璧勬簮 + resources = relationship('ResourceModel', + secondary=role_resource_table, + backref=backref('roles', lazy='dynamic')) # 璧勬簮鎵�灞炶鑹� + + def get_id(self): + return str(self.id) + + def to_dict(self): + return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")]) + + def __repr__(self): + return '<Role name:%r description:%r iconCls:%r seq:%r>\n' \ + % (self.NAME, self.DESCRIPTION, self.ICONCLS, self.SEQ) + + def to_json(self): + json = { + 'roleId': self.id, + 'createTime': self.created_at.strftime('%Y-%m-%d %H:%M:%S'), + 'updateTime': self.updated_at.strftime('%Y-%m-%d %H:%M:%S'), + 'roleName': self.name, + 'remark': self.description, + 'iconCls': self.iconCls, + 'roleSort': self.seq, + 'status': self.status, + 'roleKey': self.roleKey, + 'dataScope': self.dataScope + } + + if len(self.resources) > 0: + json['resources'] = [resource.to_json() for resource in self.resources] + + if hasattr(self, 'flag'): + json['flag'] = self.flag + + return json + + +class RoleData(BaseModel): + remark: Optional[str] = "" + roleName: str + + +class RoleStatus(BaseModel): + role_id: str + status: constr(min_length=1, max_length=1, pattern='^(0|1)$') + + +class RoleEdit(BaseModel): + role_id: str + remark: Optional[str] = "" + roleName: Optional[str] = "" + resources: Optional[list] = [] diff --git a/app/models/user_model.py b/app/models/user_model.py index 5d5d51b..81715df 100644 --- a/app/models/user_model.py +++ b/app/models/user_model.py @@ -1,6 +1,22 @@ -from sqlalchemy import Column, Integer, String +from datetime import datetime + +from sqlalchemy import Column, Integer, String, Table, ForeignKey, DateTime +from sqlalchemy.orm import relationship, backref from app.models.base_model import Base + +user_organization_table = Table('user_organization',Base.metadata + , Column('user_id', Integer, ForeignKey('user.id')) + , Column('organization_id', String(36), ForeignKey('organization.id'))) + +user_role_table = Table('user_role', Base.metadata + , Column('user_id', Integer, ForeignKey('user.id')) + , Column('role_id', String(36), ForeignKey('role.id'))) + +user_group_table = Table('user_group', Base.metadata + , Column('user_id', Integer, ForeignKey('user.id')) + , Column('group_id', Integer, ForeignKey('group.id'))) + class UserModel(Base): @@ -13,4 +29,92 @@ email = Column(String(255), nullable=False, default="") description = Column(String(255), nullable=False, default="") ragflow_id = Column(String(32), unique=True, index=True) - bisheng_id = Column(Integer, unique=True, index=True) \ No newline at end of file + bisheng_id = Column(Integer, unique=True, index=True) + login_name = Column(String(100)) + status = Column(String(10), nullable=False, default="1") + creator = Column(String(36)) + sex = Column(String(1)) + age = Column(Integer) + created_at = Column(DateTime, default=datetime.now()) + updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now()) + + + organizations = relationship('OrganizationModel', + secondary=user_organization_table, + backref=backref('users', lazy='dynamic')) + + roles = relationship('RoleModel', + secondary=user_role_table, + backref=backref('users', lazy='dynamic')) + + groups = relationship('GroupModel', + secondary=user_group_table, + backref=backref('users', lazy='dynamic')) + + + + def have_permission(self, url): + permissions = [] + for resource in self.resources: + permissions.extend(resource) + + if filter(lambda x: x.URL == url, permissions): + return True + + permissions = [] + for organization in self.organizations: + permissions.extend([resource for resource in organization.resources]) + + return filter(lambda x: x.NAME == url, permissions) + + def __repr__(self): + return '<User %r>\n' % (self.NAME) + + + def to_dict(self): + return { + 'userId': self.id, + 'userName': self.username, + 'loginName': self.login_name if self.login_name else "", + 'status': self.status, + } + + def to_json(self): + json = { + 'userId': self.id, + 'createTime': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else "", + 'updateTime': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else "", + 'userName': self.username, + 'loginName': self.login_name, + 'sex': self.sex, + 'age': self.age, + "status": self.status, + 'photo': self.phone, + 'email': self.email, + # 'phoneNumber': self.phone_number + } + + if len(self.organizations) > 0: + json['dept'] = [organization.to_json() for organization in self.organizations] + + + json['groups'] = [group.to_dict() for group in self.groups] + + roles = [] + # if len(self.roles.all()) > 0: + # roles = [role.to_json() for role in self.roles] + # organization_roles = [role.to_json() for organization in self.organizations for role in + # organization.role_list] + # for role in organization_roles: + # if role not in roles: + # roles.append(role) + json['roles'] = roles + return json + + def get_children(self): + """閫掑綊鑾峰彇鎸囧畾鐢ㄦ埛鐨勫瓙鐢ㄦ埛ID鍒楄〃""" + users = UserModel.query.filter_by(CREATOR=self.ID).all() + user_id_list = [user.ID for user in users] + for user in users: + user_id_list.extend(user.get_children()) + return user_id_list \ No newline at end of file diff --git a/app/service/group.py b/app/service/group.py index dbe7752..7519f65 100644 --- a/app/service/group.py +++ b/app/service/group.py @@ -1,41 +1,43 @@ from sqlalchemy.testing.pickleable import Order from app.config.config import settings -from app.models.group_model import GroupInfoModel, UserGroupModel +from app.models.group_model import GroupModel from app.models.user_model import UserModel from app.service.ragflow import RagflowService from app.service.token import get_ragflow_token +from app.Log import logger async def group_list(db, page_size: int, page_index: int, keyword: str): - query = db.query(GroupInfoModel) + logger.info("-------------------------group_list----------------------------------") + query = db.query(GroupModel) if keyword: - query = query.filter(GroupInfoModel.group_name.like('%{}%'.format(keyword))) - items = query.order_by(GroupInfoModel.group_id.desc()).limit(page_size).offset( + query = query.filter(GroupModel.group_name.like('%{}%'.format(keyword))) + items = query.order_by(GroupModel.id.desc()).limit(page_size).offset( (page_index - 1) * page_size).all() - items_list = [item.to_dict() for item in items] - groups = [i["id"] for i in items_list] - group_dict = {} - for group_user in db.query(UserGroupModel.group_id, UserModel.id, UserModel.username).outerjoin(UserModel, - UserModel.id == UserGroupModel.user_id).filter( - UserGroupModel.group_id.in_(groups)).all(): - if group_user.group_id in group_dict: - group_dict[group_user.group_id].append({"user_id": group_user.id, "user_name": group_user.username}) - else: - group_dict[group_user.group_id] = [{"user_id": group_user.id, "user_name": group_user.username}] - for item in items_list: - item["users"] = group_dict.get(item["id"], []) - return {"total": query.count(), "items": items_list} + # items_list = [item.to_dict() for item in items] + # groups = [i["id"] for i in items_list] + # group_dict = {} + # for group_user in db.query(GroupModel.group_id, UserModel.id, UserModel.username).outerjoin(UserModel, + # UserModel.id == UserGroupModel.user_id).filter( + # GroupModel.group_id.in_(groups)).all(): + # if group_user.group_id in group_dict: + # group_dict[group_user.group_id].append({"user_id": group_user.id, "user_name": group_user.username}) + # else: + # group_dict[group_user.group_id] = [{"user_id": group_user.id, "user_name": group_user.username}] + # for item in items_list: + # item["users"] = group_dict.get(item["id"], []) + return {"total": query.count(), "items": [item.to_json() for item in items]} async def create_group(db, group_name: str, group_description: str): try: - group_model = GroupInfoModel(group_name=group_name, group_description=group_description) + group_model = GroupModel(group_name=group_name, group_description=group_description) db.add(group_model) db.commit() db.refresh(group_model) except Exception as e: - print(e) + logger.error(e) db.rollback() return False return True @@ -43,10 +45,10 @@ async def edit_group_data(db, group_id: int, data): try: - db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group_id).update(data) + db.query(GroupModel).filter(GroupModel.id == group_id).update(data) db.commit() except Exception as e: - print(e) + logger.error(e) db.rollback() return False return True @@ -54,10 +56,10 @@ async def delete_group_data(db, group_id: int): try: - db.query(GroupInfoModel).filter(GroupInfoModel.group_id == group_id).delete() + db.query(GroupModel).filter(GroupModel.id == group_id).delete() db.commit() except Exception as e: - print(e) + logger.error(e) db.rollback() return False return True @@ -66,8 +68,8 @@ async def get_group_users(db, group_id): not_group_user = [] in_group_user = [] - user_list = [i.user_id for i in - db.query(UserGroupModel.user_id).filter(UserGroupModel.group_id.__eq__(group_id)).all()] + user_list = [u.id for i in + db.query(GroupModel).filter(GroupModel.id.__eq__(group_id)).all() for u in i.users] for u in db.query(UserModel.id, UserModel.username).order_by(UserModel.id.desc()).all(): if u.id in user_list: in_group_user.append({"user_id": u.id, "user_name": u.username}) @@ -77,8 +79,9 @@ async def save_user_to_group(db, user_id, group_id, user_list): - group_user_list = [i.user_id for i in - db.query(UserGroupModel.user_id).filter(UserGroupModel.group_id.__eq__(group_id)).all()] + group_user_list = [u.id for i in + db.query(GroupModel).filter(GroupModel.id.__eq__(group_id)).all() for u in i.users] + # print(group_user_list) new_users = set([i for i in user_list if i not in group_user_list]) delete_user = [i for i in group_user_list if i not in user_list] if new_users: @@ -105,15 +108,14 @@ user_dict[user2]["email"], user_dict[user2]["rg_id"]) except Exception as e: - print(e) + logger.error(e) return False + try: - for user in new_users: - db_user = UserGroupModel(group_id=group_id, user_id=user) - db.add(db_user) - db.query(UserGroupModel).filter(UserGroupModel.group_id.__eq__(group_id), UserGroupModel.user_id.in_(delete_user)).delete() + group = db.query(GroupModel).filter(GroupModel.id.__eq__(group_id)).first() + group.users = [db.get(UserModel, user) for user in user_list] db.commit() except Exception as e: - print(e) + logger.error(e) return False return True \ No newline at end of file diff --git a/app/service/organization.py b/app/service/organization.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/service/organization.py diff --git a/app/service/ragflow.py b/app/service/ragflow.py index 94bd9ac..1677ed5 100644 --- a/app/service/ragflow.py +++ b/app/service/ragflow.py @@ -1,7 +1,5 @@ import httpx from typing import Union, Dict, List - -from Tools.scripts.objgraph import ignore from fastapi import HTTPException from starlette import status @@ -152,10 +150,7 @@ url = f"{self.base_url}/v1/tenant/{tenant_id}/user" headers = {"Authorization": token} data = {"email": email, "user_id": user_id} - print(url) - print(data) async with httpx.AsyncClient(timeout=60) as client: response = await client.post(url, headers=headers, json=data) - print(response.text) if response.status_code != 200: raise Exception(f"Ragflow add user to tenant failed: {response.text}") diff --git a/app/service/role.py b/app/service/role.py new file mode 100644 index 0000000..5568114 --- /dev/null +++ b/app/service/role.py @@ -0,0 +1,65 @@ +import uuid + +from app.Log import logger +from app.models.resource_model import ResourceModel, ResourceTypeModel +from app.models.role_model import RoleModel + + + +async def role_list(db, page_size: int, page_index: int, keyword: str): + query = db.query(RoleModel) + if keyword: + query = query.filter(RoleModel.name.like('%{}%'.format(keyword))) + roles = query.order_by(RoleModel.id.desc()).limit(page_size).offset( + (page_index - 1) * page_size).all() + return {"total": query.count(), "rows": [role.to_json() for role in roles]} + + +async def create_role(db, role_name: str, description: str, user_id): + try: + role_model = RoleModel(id=str(uuid.uuid4()),name=role_name, description=description,creator=user_id) + db.add(role_model) + db.commit() + db.refresh(role_model) + except Exception as e: + logger.error(e) + db.rollback() + return False + return True + +async def delete_role_data(db, role_id: str): + try: + db.query(RoleModel).filter(RoleModel.id == role_id).delete() + db.commit() + except Exception as e: + logger.error(e) + db.rollback() + return False + return True + + +async def edit_role_status(db, status: str, role_id: str): + try: + db.query(RoleModel).filter(RoleModel.id == role_id).update({"status":status}) + db.commit() + except Exception as e: + logger.error(e) + db.rollback() + return False + return True + + +async def edit_role_resource(db, role_id:str, role_name:str, description:str, resources:list): + try: + role = db.query(RoleModel).filter(RoleModel.id == role_id).first() + if role_name: role.name = role_name + if description: role.description = description + if resources: + role.resources = [db.get(ResourceModel, resourcesId) for resourcesId in resources] + db.add(role) + db.commit() + except Exception as e: + logger.error(e) + db.rollback() + return False + return True \ No newline at end of file diff --git a/app/service/user.py b/app/service/user.py new file mode 100644 index 0000000..472c77a --- /dev/null +++ b/app/service/user.py @@ -0,0 +1,11 @@ +from app.models.user_model import UserModel +from app.Log import logger + +async def get_user_list(db, page_size: int, page_index: int, keyword: str): + logger.error("ffffffff") + query = db.query(UserModel) + if keyword: + query = query.filter(UserModel.group_name.like('%{}%'.format(keyword))) + users = query.order_by(UserModel.id.desc()).limit(page_size).offset( + (page_index - 1) * page_size).all() + return {"total": query.count(), "rows": [user.to_json() for user in users]} diff --git a/main.py b/main.py index 2c8564f..5826d6f 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ from contextlib import asynccontextmanager - from fastapi import FastAPI + +from app.Log import init_log from app.api.auth import router as auth_router from app.api.chat import router as chat_router from app.api.agent import router as agent_router @@ -9,6 +10,7 @@ from app.api.report import router as report_router from app.api.user import user_router from app.api.group import group_router +from app.api.role import role_router from app.models.base_model import init_db from app.task.fetch_agent import sync_agents, initialize_agents @@ -32,6 +34,7 @@ lifespan=lifespan ) + app.include_router(auth_router, prefix='/api/auth', tags=["auth"]) app.include_router(chat_router, prefix='/api/chat', tags=["chat"]) app.include_router(agent_router, prefix='/api/agent', tags=["agent"]) @@ -40,6 +43,7 @@ app.include_router(report_router, prefix='/api/report', tags=["report"]) app.include_router(user_router, prefix='/api/user', tags=["user"]) app.include_router(group_router, prefix='/api/group', tags=["group"]) +app.include_router(role_router, prefix='/api/role', tags=["role"]) if __name__ == "__main__": import uvicorn -- Gitblit v1.8.0