From d78d14282987756d1e77570cb6f5832de7800ba8 Mon Sep 17 00:00:00 2001 From: z30057876 Date: Wed, 23 Jul 2025 16:32:00 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=95=B4=E7=90=86=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/main.py | 4 +- apps/routers/appcenter.py | 25 +++++---- apps/routers/auth.py | 48 +++++++++-------- apps/routers/blacklist.py | 52 ++++++++++-------- apps/routers/chat.py | 26 ++++----- apps/routers/comment.py | 12 ++--- apps/routers/document.py | 46 +++++++--------- apps/routers/knowledge.py | 24 ++++----- apps/routers/llm.py | 40 ++++++++------ apps/routers/personal_token.py | 28 +++++----- apps/routers/service.py | 76 ++++++++++++--------------- apps/scheduler/scheduler/message.py | 1 - apps/scheduler/scheduler/scheduler.py | 1 - apps/schemas/flow_topology.py | 3 +- apps/schemas/task.py | 1 - 15 files changed, 196 insertions(+), 191 deletions(-) diff --git a/apps/main.py b/apps/main.py index 356e7d5f0..f132e9c25 100644 --- a/apps/main.py +++ b/apps/main.py @@ -62,10 +62,12 @@ app.include_router(comment.router) app.include_router(record.router) app.include_router(health.router) app.include_router(chat.router) -app.include_router(blacklist.router) +app.include_router(blacklist.admin_router) +app.include_router(blacklist.user_router) app.include_router(document.router) app.include_router(knowledge.router) app.include_router(llm.router) +app.include_router(llm.admin_router) app.include_router(mcp_service.router) app.include_router(flow.router) app.include_router(user.router) diff --git a/apps/routers/appcenter.py b/apps/routers/appcenter.py index 0ec4db915..3230e4dc2 100644 --- a/apps/routers/appcenter.py +++ b/apps/routers/appcenter.py @@ -4,10 +4,10 @@ import logging from typing import Annotated -from fastapi import APIRouter, Body, Depends, Path, Query, status +from fastapi import APIRouter, Body, Depends, Path, Query, Request, status from fastapi.responses import JSONResponse -from apps.dependency.user import get_user, verify_user +from apps.dependency.user import verify_session from apps.exceptions import InstancePermissionError from apps.schemas.appcenter import AppFlowInfo, AppPermissionData from apps.schemas.enum_var import AppFilterType, AppType @@ -30,13 +30,13 @@ logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/app", tags=["appcenter"], - dependencies=[Depends(verify_user)], + dependencies=[Depends(verify_session)], ) @router.get("", response_model=GetAppListRsp | ResponseData) async def get_applications( # noqa: PLR0913 - user_sub: Annotated[str, Depends(get_user)], + request: Request, *, my_app: Annotated[bool, Query(..., alias="createdByMe", description="筛选我创建的")] = False, my_fav: Annotated[bool, Query(..., alias="favorited", description="筛选我收藏的")] = False, @@ -45,6 +45,7 @@ async def get_applications( # noqa: PLR0913 page: Annotated[int, Query(..., alias="page", ge=1, description="页码")] = 1, ) -> JSONResponse: """获取应用列表""" + user_sub: str = request.state.user_sub if my_app and my_fav: # 只能同时使用一个过滤条件 return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -89,10 +90,12 @@ async def get_applications( # noqa: PLR0913 @router.post("", response_model=BaseAppOperationRsp | ResponseData) async def create_or_update_application( + raw_request: Request, request: Annotated[CreateAppRequest, Body(...)], - user_sub: Annotated[str, Depends(get_user)], ) -> JSONResponse: """创建或更新应用""" + user_sub: str = raw_request.state.user_sub + app_id = request.app_id if app_id: # 更新应用 try: @@ -152,10 +155,11 @@ async def create_or_update_application( @router.get("/recent", response_model=GetRecentAppListRsp | ResponseData) async def get_recently_used_applications( - user_sub: Annotated[str, Depends(get_user)], + request: Request, count: Annotated[int, Query(..., ge=1, le=10)] = 5, ) -> JSONResponse: """获取最近使用的应用""" + user_sub: str = request.state.user_sub try: recent_apps = await AppCenterManager.get_recently_used_apps(count, user_sub) except Exception: @@ -245,10 +249,11 @@ async def get_application( response_model=BaseAppOperationRsp | ResponseData, ) async def delete_application( + request: Request, app_id: Annotated[str, Path(..., alias="appId", description="应用ID")], - user_sub: Annotated[str, Depends(get_user)], ) -> JSONResponse: """删除应用""" + user_sub: str = request.state.user_sub try: await AppCenterManager.delete_app(app_id, user_sub) except ValueError: @@ -293,10 +298,11 @@ async def delete_application( @router.post("/{appId}", response_model=BaseAppOperationRsp) async def publish_application( + request: Request, app_id: Annotated[str, Path(..., alias="appId", description="应用ID")], - user_sub: Annotated[str, Depends(get_user)], ) -> JSONResponse: """发布应用""" + user_sub: str = request.state.user_sub try: published = await AppCenterManager.update_app_publish_status(app_id, user_sub) if not published: @@ -341,11 +347,12 @@ async def publish_application( @router.put("/{appId}", response_model=ModFavAppRsp | ResponseData) async def modify_favorite_application( + raw_request: Request, app_id: Annotated[str, Path(..., alias="appId", description="应用ID")], request: Annotated[ModFavAppRequest, Body(...)], - user_sub: Annotated[str, Depends(get_user)], ) -> JSONResponse: """更改应用收藏状态""" + user_sub: str = raw_request.state.user_sub try: await AppCenterManager.modify_favorite_app(app_id, user_sub, favorited=request.favorited) except ValueError: diff --git a/apps/routers/auth.py b/apps/routers/auth.py index 3cd5def62..1d5cc5160 100644 --- a/apps/routers/auth.py +++ b/apps/routers/auth.py @@ -3,14 +3,14 @@ import logging from pathlib import Path -from typing import Annotated from fastapi import APIRouter, Depends, Request, status from fastapi.responses import HTMLResponse, JSONResponse from fastapi.templating import Jinja2Templates +from apps.common.config import config from apps.common.oidc import oidc_provider -from apps.dependency import get_session, get_user, verify_user +from apps.dependency import verify_personal_token, verify_session from apps.schemas.response_data import ( AuthUserMsg, AuthUserRsp, @@ -26,8 +26,12 @@ router = APIRouter( prefix="/api/auth", tags=["auth"], ) +user_router = APIRouter( + prefix="/api/user", + tags=["user"], + dependencies=[Depends(verify_session), Depends(verify_personal_token)], +) logger = logging.getLogger(__name__) - templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates") @@ -56,7 +60,13 @@ async def oidc_login(request: Request, code: str) -> HTMLResponse: status_code=status_code, ) - user_host = request.client.host if request.client else None + if not request.client: + return templates.TemplateResponse( + "login_failed.html.j2", + {"request": request, "reason": "无法获取用户信息,请关闭本窗口并重试。"}, + status_code=status.HTTP_403_FORBIDDEN, + ) + user_host = request.client.host if not user_sub: logger.error("OIDC no user_sub associated.") @@ -76,12 +86,8 @@ async def oidc_login(request: Request, code: str) -> HTMLResponse: # 用户主动logout -@router.get("/logout", response_model=ResponseData) -async def logout( - request: Request, - user_sub: Annotated[str, Depends(get_user)], - session_id: Annotated[str, Depends(get_session)], -) -> JSONResponse: +@user_router.get("/logout", response_model=ResponseData) +async def logout(request: Request) -> JSONResponse: """用户登出EulerCopilot""" if not request.client: return JSONResponse( @@ -92,8 +98,10 @@ async def logout( result={}, ).model_dump(exclude_none=True, by_alias=True), ) - await TokenManager.delete_plugin_token(user_sub) - await SessionManager.delete_session(session_id) + await TokenManager.delete_plugin_token(request.state.user_sub) + + if hasattr(request.state, "session_id"): + await SessionManager.delete_session(request.state.session_id) return JSONResponse( status_code=status.HTTP_200_OK, @@ -120,7 +128,7 @@ async def oidc_redirect() -> JSONResponse: # TODO(zwt): OIDC主动触发logout -@router.post("/logout", dependencies=[Depends(verify_user)], response_model=ResponseData) +@router.post("/logout", response_model=ResponseData) async def oidc_logout(token: str) -> JSONResponse: """OIDC主动触发登出""" return JSONResponse( @@ -133,13 +141,10 @@ async def oidc_logout(token: str) -> JSONResponse: ) -@router.get("/user", response_model=AuthUserRsp) -async def userinfo( - user_sub: Annotated[str, Depends(get_user)], - _: Annotated[None, Depends(verify_user)], -) -> JSONResponse: +@user_router.get("/user", response_model=AuthUserRsp) +async def userinfo(request: Request) -> JSONResponse: """获取用户信息""" - user = await UserManager.get_user(user_sub=user_sub) + user = await UserManager.get_user(user_sub=request.state.user_sub) if not user: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -149,15 +154,16 @@ async def userinfo( result={}, ).model_dump(exclude_none=True, by_alias=True), ) + is_admin = user.userSub in config.login.admin_user return JSONResponse( status_code=status.HTTP_200_OK, content=AuthUserRsp( code=status.HTTP_200_OK, message="success", result=AuthUserMsg( - user_sub=user_sub, + user_sub=request.state.user_sub, revision=user.isActive, - is_admin=user.isAdmin, + is_admin=is_admin, ), ).model_dump(exclude_none=True, by_alias=True), ) diff --git a/apps/routers/blacklist.py b/apps/routers/blacklist.py index 53697a9b2..be8efbfb6 100644 --- a/apps/routers/blacklist.py +++ b/apps/routers/blacklist.py @@ -1,12 +1,10 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """FastAPI 黑名单相关路由""" -from typing import Annotated - -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, Request, status from fastapi.responses import JSONResponse -from apps.dependency.user import get_user, verify_user +from apps.dependency.user import verify_admin, verify_personal_token, verify_session from apps.schemas.request_data import ( AbuseProcessRequest, AbuseRequest, @@ -26,17 +24,29 @@ from apps.services.blacklist import ( UserBlacklistManager, ) -router = APIRouter( +admin_router = APIRouter( + prefix="/api/blacklist", + tags=["blacklist"], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + Depends(verify_admin), + ], +) +user_router = APIRouter( prefix="/api/blacklist", tags=["blacklist"], - dependencies=[Depends(verify_user)], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], ) PAGE_SIZE = 20 MAX_CREDIT = 100 -@router.get("/user", response_model=GetBlacklistUserRsp) -async def get_blacklist_user(page: int = 0): # noqa: ANN201 +@admin_router.get("/user", response_model=GetBlacklistUserRsp) +async def get_blacklist_user(page: int = 0) -> JSONResponse: """获取黑名单用户""" # 计算分页 user_list = await UserBlacklistManager.get_blacklisted_users( @@ -51,8 +61,8 @@ async def get_blacklist_user(page: int = 0): # noqa: ANN201 ).model_dump(exclude_none=True, by_alias=True)) -@router.post("/user", response_model=ResponseData) -async def change_blacklist_user(request: UserBlacklistRequest): # noqa: ANN201 +@admin_router.post("/user", response_model=ResponseData) +async def change_blacklist_user(request: UserBlacklistRequest) -> JSONResponse: """操作黑名单用户""" # 拉黑用户 if request.is_ban: @@ -79,8 +89,8 @@ async def change_blacklist_user(request: UserBlacklistRequest): # noqa: ANN201 result={}, ).model_dump(exclude_none=True, by_alias=True)) -@router.get("/question", response_model=GetBlacklistQuestionRsp) -async def get_blacklist_question(page: int = 0): # noqa: ANN201 +@admin_router.get("/question", response_model=GetBlacklistQuestionRsp) +async def get_blacklist_question(page: int = 0) -> JSONResponse: """ 获取黑名单问题 @@ -98,8 +108,8 @@ async def get_blacklist_question(page: int = 0): # noqa: ANN201 result=GetBlacklistQuestionMsg(question_list=question_list), ).model_dump(exclude_none=True, by_alias=True)) -@router.post("/question", response_model=ResponseData) -async def change_blacklist_question(request: QuestionBlacklistRequest): # noqa: ANN201 +@admin_router.post("/question", response_model=ResponseData) +async def change_blacklist_question(request: QuestionBlacklistRequest) -> JSONResponse: """黑名单问题检测或操作""" # 删问题 if request.is_deletion: @@ -131,11 +141,11 @@ async def change_blacklist_question(request: QuestionBlacklistRequest): # noqa: ).model_dump(exclude_none=True, by_alias=True)) -@router.post("/complaint", response_model=ResponseData) -async def abuse_report(request: AbuseRequest, user_sub: Annotated[str, Depends(get_user)]): # noqa: ANN201 +@admin_router.post("/complaint", response_model=ResponseData) +async def abuse_report(raw_request: Request, request: AbuseRequest) -> JSONResponse: """用户实施举报""" result = await AbuseManager.change_abuse_report( - user_sub, + raw_request.state.user_sub, request.record_id, request.reason_type, request.reason, @@ -154,8 +164,8 @@ async def abuse_report(request: AbuseRequest, user_sub: Annotated[str, Depends(g ).model_dump(exclude_none=True, by_alias=True)) -@router.get("/abuse", response_model=GetBlacklistQuestionRsp) -async def get_abuse_report(page: int = 0): # noqa: ANN201 +@admin_router.get("/abuse", response_model=GetBlacklistQuestionRsp) +async def get_abuse_report(page: int = 0) -> JSONResponse: """获取待审核的问答对""" # 此处前端需记录ID result = await QuestionBlacklistManager.get_blacklisted_questions( @@ -169,8 +179,8 @@ async def get_abuse_report(page: int = 0): # noqa: ANN201 result=GetBlacklistQuestionMsg(question_list=result), ).model_dump(exclude_none=True, by_alias=True)) -@router.post("/abuse", response_model=ResponseData) -async def change_abuse_report(request: AbuseProcessRequest): # noqa: ANN201 +@admin_router.post("/abuse", response_model=ResponseData) +async def change_abuse_report(request: AbuseProcessRequest) -> JSONResponse: """对被举报问答对进行操作""" if request.is_deletion: result = await AbuseManager.audit_abuse_report( diff --git a/apps/routers/chat.py b/apps/routers/chat.py index 7fe5162c0..1db26e317 100644 --- a/apps/routers/chat.py +++ b/apps/routers/chat.py @@ -3,16 +3,14 @@ import asyncio import logging -import uuid from collections.abc import AsyncGenerator -from typing import Annotated -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi.responses import JSONResponse, StreamingResponse from apps.common.queue import MessageQueue from apps.common.wordscheck import WordsCheck -from apps.dependency import get_session, get_user +from apps.dependency import verify_personal_token, verify_session from apps.scheduler.scheduler import Scheduler from apps.scheduler.scheduler.context import save_data from apps.schemas.request_data import RequestData @@ -28,19 +26,19 @@ logger = logging.getLogger(__name__) router = APIRouter( prefix="/api", tags=["chat"], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], ) async def init_task(post_body: RequestData, user_sub: str, session_id: str) -> Task: """初始化Task""" - # 生成group_id - if not post_body.group_id: - post_body.group_id = str(uuid.uuid4()) # 创建或还原Task task = await TaskManager.get_task(session_id=session_id, post_body=post_body, user_sub=user_sub) # 更改信息并刷新数据库 task.runtime.question = post_body.question - task.ids.group_id = post_body.group_id return task @@ -111,12 +109,10 @@ async def chat_generator(post_body: RequestData, user_sub: str, session_id: str) @router.post("/chat") -async def chat( - post_body: RequestData, - user_sub: Annotated[str, Depends(get_user)], - session_id: Annotated[str, Depends(get_session)], -) -> StreamingResponse: +async def chat(request: Request, post_body: RequestData) -> StreamingResponse: """LLM流式对话接口""" + user_sub = request.state.user_sub + session_id = request.state.session_id # 问题黑名单检测 if not await QuestionBlacklistManager.check_blacklisted_questions(input_question=post_body.question): # 用户扣分 @@ -138,9 +134,9 @@ async def chat( @router.post("/stop", response_model=ResponseData) -async def stop_generation(user_sub: Annotated[str, Depends(get_user)]): # noqa: ANN201 +async def stop_generation(request: Request) -> JSONResponse: """停止生成""" - await Activity.remove_active(user_sub) + await Activity.remove_active(request.state.user_sub) return JSONResponse( status_code=status.HTTP_200_OK, content=ResponseData( diff --git a/apps/routers/comment.py b/apps/routers/comment.py index 856abd2a9..93a9edb63 100644 --- a/apps/routers/comment.py +++ b/apps/routers/comment.py @@ -3,12 +3,11 @@ import logging from datetime import UTC, datetime -from typing import Annotated -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, Request, status from fastapi.responses import JSONResponse -from apps.dependency import get_user, verify_user +from apps.dependency import verify_personal_token, verify_session from apps.schemas.record import RecordComment from apps.schemas.request_data import AddCommentData from apps.schemas.response_data import ResponseData @@ -19,13 +18,14 @@ router = APIRouter( prefix="/api/comment", tags=["comment"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) @router.post("", response_model=ResponseData) -async def add_comment(post_body: AddCommentData, user_sub: Annotated[str, Depends(get_user)]) -> JSONResponse: +async def add_comment(request: Request, post_body: AddCommentData) -> JSONResponse: """给Record添加评论""" comment_data = RecordComment( comment=post_body.comment, @@ -34,7 +34,7 @@ async def add_comment(post_body: AddCommentData, user_sub: Annotated[str, Depend reason_description=post_body.reason_description, feedback_time=round(datetime.now(tz=UTC).timestamp(), 3), ) - result = await CommentManager.update_comment(post_body.record_id, comment_data, user_sub) + result = await CommentManager.update_comment(post_body.record_id, comment_data, request.state.user_sub) if not result: return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=ResponseData( code=status.HTTP_400_BAD_REQUEST, diff --git a/apps/routers/document.py b/apps/routers/document.py index c940dfa79..86e96e0f9 100644 --- a/apps/routers/document.py +++ b/apps/routers/document.py @@ -5,12 +5,11 @@ Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """ import uuid -from typing import Annotated -from fastapi import APIRouter, Depends, File, Query, UploadFile, status +from fastapi import APIRouter, Depends, Request, UploadFile, status from fastapi.responses import JSONResponse -from apps.dependency import get_session, get_user, verify_user +from apps.dependency import verify_personal_token, verify_session from apps.schemas.enum_var import DocumentStatus from apps.schemas.response_data import ( ConversationDocumentItem, @@ -29,21 +28,17 @@ router = APIRouter( prefix="/api/document", tags=["document"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) @router.post("/{conversation_id}") -async def document_upload( # noqa: ANN201 - conversation_id: uuid.UUID, - documents: Annotated[list[UploadFile], File(...)], - user_sub: Annotated[str, Depends(get_user)], - session_id: Annotated[str, Depends(get_session)], -): +async def document_upload(request: Request, conversation_id: uuid.UUID, documents: list[UploadFile]) -> JSONResponse: """上传文档""" - result = await DocumentManager.storage_docs(user_sub, conversation_id, documents) - await KnowledgeBaseService.send_file_to_rag(session_id, result) + result = await DocumentManager.storage_docs(request.state.user_sub, conversation_id, documents) + await KnowledgeBaseService.send_file_to_rag(request.state.session_id, result) # 返回所有Framework已知的文档 succeed_document: list[UploadDocumentMsgItem] = [ @@ -67,17 +62,14 @@ async def document_upload( # noqa: ANN201 @router.get("/{conversation_id}", response_model=ConversationDocumentRsp) -async def get_document_list( # noqa: ANN201 - conversation_id: uuid.UUID, - user_sub: Annotated[str, Depends(get_user)], - session_id: Annotated[str, Depends(get_session)], +async def get_document_list( + request: Request, conversation_id: uuid.UUID, *, - used: Annotated[bool, Query()] = False, - unused: Annotated[bool, Query()] = True, -): + used: bool = False, unused: bool = True, +) -> JSONResponse: """获取文档列表""" # 判断Conversation有权访问 - if not await ConversationManager.verify_conversation_access(user_sub, conversation_id): + if not await ConversationManager.verify_conversation_access(request.state.user_sub, conversation_id): return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=ResponseData( @@ -106,7 +98,9 @@ async def get_document_list( # noqa: ANN201 if unused: # 拿到所有未使用的文档 unused_docs = await DocumentManager.get_unused_docs(conversation_id) - doc_status = await KnowledgeBaseService.get_doc_status_from_rag(session_id, [item.id for item in unused_docs]) + doc_status = await KnowledgeBaseService.get_doc_status_from_rag( + request.state.session_id, [item.id for item in unused_docs], + ) for current_doc in unused_docs: for status_item in doc_status: if current_doc.id != status_item.id: @@ -142,14 +136,10 @@ async def get_document_list( # noqa: ANN201 @router.delete("/{document_id}", response_model=ResponseData) -async def delete_single_document( # noqa: ANN201 - document_id: str, - user_sub: Annotated[str, Depends(get_user)], - session_id: Annotated[str, Depends(get_session)], -): +async def delete_single_document(request: Request, document_id: str) -> JSONResponse: """删除单个文件""" # 在Framework侧删除 - result = await DocumentManager.delete_document(user_sub, [document_id]) + result = await DocumentManager.delete_document(request.state.user_sub, [document_id]) if not result: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -160,7 +150,7 @@ async def delete_single_document( # noqa: ANN201 ).model_dump(exclude_none=True, by_alias=False), ) # 在RAG侧删除 - result = await KnowledgeBaseService.delete_doc_from_rag(session_id, [document_id]) + result = await KnowledgeBaseService.delete_doc_from_rag(request.state.session_id, [document_id]) if not result: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, diff --git a/apps/routers/knowledge.py b/apps/routers/knowledge.py index 236c23d5b..52f50082b 100644 --- a/apps/routers/knowledge.py +++ b/apps/routers/knowledge.py @@ -1,12 +1,10 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """FastAPI 用户资产库路由""" -from typing import Annotated - -from fastapi import APIRouter, Body, Depends, status +from fastapi import APIRouter, Depends, Request, status from fastapi.responses import JSONResponse -from apps.dependency import get_user, verify_user +from apps.dependency import verify_personal_token, verify_session from apps.schemas.request_data import ( UpdateKbReq, ) @@ -21,7 +19,8 @@ router = APIRouter( prefix="/api/knowledge", tags=["knowledge"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) @@ -30,26 +29,25 @@ router = APIRouter( status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) -async def list_kb(user_sub: Annotated[str, Depends(get_user)]) -> JSONResponse: +async def list_kb(request: Request) -> JSONResponse: """获取当前用户的知识库ID""" - team_kb_list = await KnowledgeBaseManager.get_selected_kb(user_sub) + kb_list = await KnowledgeBaseManager.get_selected_kb(request.state.user_sub) return JSONResponse( status_code=status.HTTP_200_OK, content=ListTeamKnowledgeRsp( code=status.HTTP_200_OK, message="success", - result=ListTeamKnowledgeMsg(teamKbList=team_kb_list), + result=ListTeamKnowledgeMsg(teamKbList=kb_list), ).model_dump(exclude_none=True, by_alias=True), ) @router.put("", response_model=ResponseData) -async def update_kb( - user_sub: Annotated[str, Depends(get_user)], - put_body: Annotated[UpdateKbReq, Body(...)], -) -> JSONResponse: +async def update_kb(request: Request, put_body: UpdateKbReq) -> JSONResponse: """更新当前用户的知识库ID""" - kb_ids_update_success = await KnowledgeBaseManager.save_selected_kb(user_sub, put_body.kb_ids) + kb_ids_update_success = await KnowledgeBaseManager.save_selected_kb( + request.state.user_sub, put_body.kb_ids, + ) return JSONResponse( status_code=status.HTTP_200_OK, content=ResponseData( diff --git a/apps/routers/llm.py b/apps/routers/llm.py index d14b4a833..b4f900648 100644 --- a/apps/routers/llm.py +++ b/apps/routers/llm.py @@ -3,10 +3,10 @@ from typing import Annotated -from fastapi import APIRouter, Depends, Query, status +from fastapi import APIRouter, Depends, Query, Request, status from fastapi.responses import JSONResponse -from apps.dependency import get_user, verify_user +from apps.dependency import verify_admin, verify_personal_token, verify_session from apps.schemas.request_data import ( UpdateLLMReq, ) @@ -21,7 +21,17 @@ router = APIRouter( prefix="/api/llm", tags=["llm"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), + ], +) +admin_router = APIRouter( + prefix="/api/llm", + tags=["llm"], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + Depends(verify_admin), ], ) @@ -43,18 +53,16 @@ async def list_llm_provider() -> JSONResponse: ) -@router.get( - "", response_model=ListLLMRsp, - responses={ +@router.get("", response_model=ListLLMRsp, responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) async def list_llm( - user_sub: Annotated[str, Depends(get_user)], + request: Request, llm_id: Annotated[str | None, Query(description="大模型ID", alias="llmId")] = None, ) -> JSONResponse: """获取大模型列表""" - llm_list = await LLMManager.list_llm(user_sub, llm_id) + llm_list = await LLMManager.list_llm(request.state.user_sub, llm_id) return JSONResponse( status_code=status.HTTP_200_OK, content=ListLLMRsp( @@ -65,19 +73,17 @@ async def list_llm( ) -@router.put( - "", - responses={ +@router.put("", responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) async def create_llm( + request: Request, req: UpdateLLMReq, - user_sub: Annotated[str, Depends(get_user)], llm_id: Annotated[str | None, Query(description="大模型ID", alias="llmId")] = None, ) -> JSONResponse: """创建或更新大模型配置""" - llm_id = await LLMManager.update_llm(user_sub, llm_id, req) + llm_id = await LLMManager.update_llm(request.state.user_sub, llm_id, req) return JSONResponse( status_code=status.HTTP_200_OK, content=ResponseData( @@ -95,11 +101,11 @@ async def create_llm( }, ) async def delete_llm( - user_sub: Annotated[str, Depends(get_user)], + request: Request, llm_id: Annotated[str, Query(description="大模型ID", alias="llmId")], ) -> JSONResponse: """删除大模型配置""" - llm_id = await LLMManager.delete_llm(user_sub, llm_id) + await LLMManager.delete_llm(request.state.user_sub, llm_id) return JSONResponse( status_code=status.HTTP_200_OK, content=ResponseData( @@ -117,12 +123,12 @@ async def delete_llm( }, ) async def update_user_llm( - user_sub: Annotated[str, Depends(get_user)], + request: Request, conversation_id: Annotated[str, Query(description="对话ID", alias="conversationId")], llm_id: Annotated[str, Query(description="llm ID", alias="llmId")] = "empty", ) -> JSONResponse: """更新对话的知识库""" - llm_id = await LLMManager.update_user_llm(user_sub, conversation_id, llm_id) + llm_id = await LLMManager.update_user_llm(request.state.user_sub, conversation_id, llm_id) return JSONResponse( status_code=status.HTTP_200_OK, content=ResponseData( diff --git a/apps/routers/personal_token.py b/apps/routers/personal_token.py index f7a5eb0be..490755abd 100644 --- a/apps/routers/personal_token.py +++ b/apps/routers/personal_token.py @@ -1,12 +1,10 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """FastAPI API Key相关路由""" -from typing import Annotated - -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, Request, status from fastapi.responses import JSONResponse -from apps.dependency.user import get_user, verify_user +from apps.dependency.user import verify_personal_token, verify_session from apps.schemas.personal_token import PostPersonalTokenMsg, PostPersonalTokenRsp from apps.schemas.response_data import ResponseData from apps.services.personal_token import PersonalTokenManager @@ -14,28 +12,30 @@ from apps.services.personal_token import PersonalTokenManager router = APIRouter( prefix="/api/auth/key", tags=["key"], - dependencies=[Depends(verify_user)], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], ) @router.post("", responses={ 400: {"model": ResponseData}, }, response_model=PostPersonalTokenRsp) -async def manage_personal_token(action: str, user_sub: Annotated[str, Depends(get_user)]) -> JSONResponse: +async def change_personal_token(request: Request) -> JSONResponse: """管理用户的API密钥""" - action = action.lower() - if action == "update": - api_key: str | None = await PersonalTokenManager.update_personal_token(user_sub) - else: - return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=ResponseData( - code=status.HTTP_400_BAD_REQUEST, - message="invalid request", + new_api_key: str | None = await PersonalTokenManager.update_personal_token(request.state.user_sub) + if not new_api_key: + return JSONResponse(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=ResponseData( + code=status.HTTP_500_INTERNAL_SERVER_ERROR, + message="failed to update personal token", result={}, ).model_dump(exclude_none=True, by_alias=True)) + return JSONResponse(status_code=status.HTTP_200_OK, content=PostPersonalTokenRsp( code=status.HTTP_200_OK, message="success", result=PostPersonalTokenMsg( - api_key=api_key, + api_key=new_api_key, ), ).model_dump(exclude_none=True, by_alias=True)) diff --git a/apps/routers/service.py b/apps/routers/service.py index 3449727e3..2fc5fddca 100644 --- a/apps/routers/service.py +++ b/apps/routers/service.py @@ -4,10 +4,10 @@ import logging from typing import Annotated -from fastapi import APIRouter, Body, Depends, Path, Query, status +from fastapi import APIRouter, Body, Depends, Path, Request, status from fastapi.responses import JSONResponse -from apps.dependency.user import get_user, verify_user +from apps.dependency.user import verify_personal_token, verify_session from apps.exceptions import InstancePermissionError, ServiceIDError from apps.schemas.enum_var import SearchType from apps.schemas.request_data import ModFavServiceRequest, UpdateServiceRequest @@ -30,23 +30,23 @@ logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/service", tags=["service-center"], - dependencies=[Depends(verify_user)], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], ) @router.get("", response_model=GetServiceListRsp | ResponseData) async def get_service_list( # noqa: PLR0913 - user_sub: Annotated[str, Depends(get_user)], + request: Request, *, - my_service: Annotated[bool, Query(..., alias="createdByMe", description="筛选我创建的")] = False, - my_fav: Annotated[bool, Query(..., alias="favorited", description="筛选我收藏的")] = False, - search_type: Annotated[SearchType, Query(..., alias="searchType", description="搜索类型")] = SearchType.ALL, - keyword: Annotated[str | None, Query(..., alias="keyword", description="搜索关键字")] = None, - page: Annotated[int, Query(..., alias="page", ge=1, description="页码")] = 1, - page_size: Annotated[int, Query(..., alias="pageSize", ge=1, le=100, description="每页数量")] = 16, + createdByMe: bool = False, favorited: bool = False, # noqa: N803 + searchType: SearchType = SearchType.ALL, keyword: str | None = None, # noqa: N803 + page: int = 1, pageSize: int = 16, # noqa: N803 ) -> JSONResponse: """获取服务列表""" - if my_service and my_fav: # 只能同时选择一个筛选条件 + if createdByMe and favorited: # 只能同时选择一个筛选条件 return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content=ResponseData( @@ -58,29 +58,29 @@ async def get_service_list( # noqa: PLR0913 service_cards, total_count = [], -1 try: - if my_service: # 筛选我创建的 + if createdByMe: # 筛选我创建的 service_cards, total_count = await ServiceCenterManager.fetch_user_services( - user_sub, - search_type, + request.state.user_sub, + searchType, keyword, page, - page_size, + pageSize, ) - elif my_fav: # 筛选我收藏的 + elif favorited: # 筛选我收藏的 service_cards, total_count = await ServiceCenterManager.fetch_favorite_services( - user_sub, - search_type, + request.state.user_sub, + searchType, keyword, page, - page_size, + pageSize, ) else: # 获取所有服务 service_cards, total_count = await ServiceCenterManager.fetch_all_services( - user_sub, - search_type, + request.state.user_sub, + searchType, keyword, page, - page_size, + pageSize, ) except Exception: logger.exception("[ServiceCenter] 获取服务列表失败") @@ -116,14 +116,11 @@ async def get_service_list( # noqa: PLR0913 @router.post("", response_model=UpdateServiceRsp) -async def update_service( - user_sub: Annotated[str, Depends(get_user)], - data: Annotated[UpdateServiceRequest, Body(..., description="上传 YAML 文本对应数据对象")], -) -> JSONResponse: +async def update_service(request: Request, data: UpdateServiceRequest) -> JSONResponse: """上传并解析服务""" if not data.service_id: try: - service_id = await ServiceCenterManager.create_service(user_sub, data.data) + service_id = await ServiceCenterManager.create_service(request.state.user_sub, data.data) except Exception as e: logger.exception("[ServiceCenter] 创建服务失败") return JSONResponse( @@ -136,7 +133,7 @@ async def update_service( ) else: try: - service_id = await ServiceCenterManager.update_service(user_sub, data.service_id, data.data) + service_id = await ServiceCenterManager.update_service(request.state.user_sub, data.service_id, data.data) except ServiceIDError: return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -184,16 +181,14 @@ async def update_service( @router.get("/{serviceId}", response_model=GetServiceDetailRsp) async def get_service_detail( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], - *, - edit: Annotated[bool, Query(..., description="是否为编辑模式")] = False, + request: Request, serviceId: str, # noqa: N803 + *, edit: bool = False, ) -> JSONResponse: """获取服务详情""" # 示例:返回指定服务的详情 if edit: try: - name, data = await ServiceCenterManager.get_service_data(user_sub, service_id) + name, data = await ServiceCenterManager.get_service_data(request.state.user_sub, serviceId) except ServiceIDError: return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -222,10 +217,10 @@ async def get_service_detail( result={}, ).model_dump(exclude_none=True, by_alias=True), ) - detail = GetServiceDetailMsg(serviceId=service_id, name=name, data=data) + detail = GetServiceDetailMsg(serviceId=serviceId, name=name, data=data) else: try: - name, apis = await ServiceCenterManager.get_service_apis(service_id) + name, apis = await ServiceCenterManager.get_service_apis(serviceId) except ServiceIDError: return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -245,19 +240,16 @@ async def get_service_detail( result={}, ).model_dump(exclude_none=True, by_alias=True), ) - detail = GetServiceDetailMsg(serviceId=service_id, name=name, apis=apis) + detail = GetServiceDetailMsg(serviceId=serviceId, name=name, apis=apis) rsp = GetServiceDetailRsp(code=status.HTTP_200_OK, message="OK", result=detail) return JSONResponse(status_code=status.HTTP_200_OK, content=rsp.model_dump(exclude_none=True, by_alias=True)) @router.delete("/{serviceId}", response_model=DeleteServiceRsp) -async def delete_service( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], -) -> JSONResponse: +async def delete_service(request: Request, serviceId: str) -> JSONResponse: # noqa: N803 """删除服务""" try: - await ServiceCenterManager.delete_service(user_sub, service_id) + await ServiceCenterManager.delete_service(request.state.user_sub, serviceId) except ServiceIDError: return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -286,7 +278,7 @@ async def delete_service( result={}, ).model_dump(exclude_none=True, by_alias=True), ) - msg = BaseServiceOperationMsg(serviceId=service_id) + msg = BaseServiceOperationMsg(serviceId=serviceId) rsp = DeleteServiceRsp(code=status.HTTP_200_OK, message="OK", result=msg) return JSONResponse(status_code=status.HTTP_200_OK, content=rsp.model_dump(exclude_none=True, by_alias=True)) diff --git a/apps/scheduler/scheduler/message.py b/apps/scheduler/scheduler/message.py index c7340562d..3e9ffca11 100644 --- a/apps/scheduler/scheduler/message.py +++ b/apps/scheduler/scheduler/message.py @@ -8,7 +8,6 @@ from textwrap import dedent from apps.common.config import config from apps.common.queue import MessageQueue from apps.models.document import Document -from apps.schemas.collection import LLM from apps.schemas.enum_var import EventType from apps.schemas.message import ( DocumentAddContent, diff --git a/apps/scheduler/scheduler/scheduler.py b/apps/scheduler/scheduler/scheduler.py index 59376ada5..dc9dc47ac 100644 --- a/apps/scheduler/scheduler/scheduler.py +++ b/apps/scheduler/scheduler/scheduler.py @@ -5,7 +5,6 @@ import logging from datetime import UTC, datetime from apps.common.config import config -from apps.common.mongo import MongoDB from apps.common.queue import MessageQueue from apps.scheduler.executor.agent import MCPAgentExecutor from apps.scheduler.executor.flow import FlowExecutor diff --git a/apps/schemas/flow_topology.py b/apps/schemas/flow_topology.py index 17f4baba2..120184422 100644 --- a/apps/schemas/flow_topology.py +++ b/apps/schemas/flow_topology.py @@ -1,6 +1,7 @@ # Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """前端展示flow用到的数据结构""" +import uuid from typing import Any from pydantic import BaseModel, Field @@ -49,7 +50,7 @@ class NodeItem(BaseModel): step_id: str = Field(alias="stepId", default="") service_id: str = Field(alias="serviceId", default="") - node_id: str = Field(alias="nodeId", default="") + node_id: uuid.UUID = Field(alias="nodeId", default=uuid.UUID("00000000-0000-0000-0000-000000000000")) name: str = Field(default="") call_id: str = Field(alias="callId", default="Empty") description: str = Field(default="") diff --git a/apps/schemas/task.py b/apps/schemas/task.py index ac49e6083..555eade9f 100644 --- a/apps/schemas/task.py +++ b/apps/schemas/task.py @@ -51,7 +51,6 @@ class TaskIds(BaseModel): """任务涉及的各种ID""" session_id: str = Field(description="会话ID") - group_id: str = Field(description="组ID") conversation_id: uuid.UUID = Field(description="对话ID") record_id: str = Field(description="记录ID", default_factory=lambda: str(uuid.uuid4())) user_sub: str = Field(description="用户ID") -- Gitee From a9ed428327eab8bf6dde2e41136d0486c410fe6e Mon Sep 17 00:00:00 2001 From: z30057876 Date: Thu, 24 Jul 2025 11:12:54 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=88=87=E5=88=86=E5=B9=B6=E6=95=B4?= =?UTF-8?q?=E7=90=86=E8=B7=AF=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/routers/blacklist.py | 2 +- apps/routers/conversation.py | 4 +- apps/routers/document.py | 15 +++++-- apps/routers/flow.py | 79 ++++++++++++--------------------- apps/routers/llm.py | 8 ++-- apps/routers/mcp_service.py | 86 +++++++++++++++++------------------- apps/routers/record.py | 25 ++++++----- apps/routers/service.py | 20 +++++---- apps/routers/tag.py | 2 +- 9 files changed, 114 insertions(+), 127 deletions(-) diff --git a/apps/routers/blacklist.py b/apps/routers/blacklist.py index be8efbfb6..1e0af9f5c 100644 --- a/apps/routers/blacklist.py +++ b/apps/routers/blacklist.py @@ -141,7 +141,7 @@ async def change_blacklist_question(request: QuestionBlacklistRequest) -> JSONRe ).model_dump(exclude_none=True, by_alias=True)) -@admin_router.post("/complaint", response_model=ResponseData) +@user_router.post("/complaint", response_model=ResponseData) async def abuse_report(raw_request: Request, request: AbuseRequest) -> JSONResponse: """用户实施举报""" result = await AbuseManager.change_abuse_report( diff --git a/apps/routers/conversation.py b/apps/routers/conversation.py index 08413634e..23c36b62d 100644 --- a/apps/routers/conversation.py +++ b/apps/routers/conversation.py @@ -6,10 +6,10 @@ from datetime import datetime from typing import Annotated import pytz -from fastapi import APIRouter, Body, Depends, Query, Request, status +from fastapi import APIRouter, Body, Depends, Query, status from fastapi.responses import JSONResponse -from apps.dependency import get_user, verify_user +from apps.dependency import verify_admin, verify_personal_token, verify_session from apps.models.conversation import Conversation from apps.schemas.request_data import ( DeleteConversationData, diff --git a/apps/routers/document.py b/apps/routers/document.py index 86e96e0f9..ea87fcbda 100644 --- a/apps/routers/document.py +++ b/apps/routers/document.py @@ -5,8 +5,9 @@ Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. """ import uuid +from typing import Annotated -from fastapi import APIRouter, Depends, Request, UploadFile, status +from fastapi import APIRouter, Depends, Path, Request, UploadFile, status from fastapi.responses import JSONResponse from apps.dependency import verify_personal_token, verify_session @@ -35,7 +36,11 @@ router = APIRouter( @router.post("/{conversation_id}") -async def document_upload(request: Request, conversation_id: uuid.UUID, documents: list[UploadFile]) -> JSONResponse: +async def document_upload( + request: Request, + conversation_id: Annotated[uuid.UUID, Path()], + documents: list[UploadFile], +) -> JSONResponse: """上传文档""" result = await DocumentManager.storage_docs(request.state.user_sub, conversation_id, documents) await KnowledgeBaseService.send_file_to_rag(request.state.session_id, result) @@ -63,7 +68,7 @@ async def document_upload(request: Request, conversation_id: uuid.UUID, document @router.get("/{conversation_id}", response_model=ConversationDocumentRsp) async def get_document_list( - request: Request, conversation_id: uuid.UUID, + request: Request, conversation_id: Annotated[uuid.UUID, Path()], *, used: bool = False, unused: bool = True, ) -> JSONResponse: @@ -136,7 +141,9 @@ async def get_document_list( @router.delete("/{document_id}", response_model=ResponseData) -async def delete_single_document(request: Request, document_id: str) -> JSONResponse: +async def delete_single_document( + request: Request, document_id: Annotated[str, Path()], +) -> JSONResponse: """删除单个文件""" # 在Framework侧删除 result = await DocumentManager.delete_document(request.state.user_sub, [document_id]) diff --git a/apps/routers/flow.py b/apps/routers/flow.py index 0e1d00951..3502087f9 100644 --- a/apps/routers/flow.py +++ b/apps/routers/flow.py @@ -3,11 +3,10 @@ from typing import Annotated -from fastapi import APIRouter, Body, Depends, Query, status +from fastapi import APIRouter, Body, Depends, Query, Request, status from fastapi.responses import JSONResponse -from apps.dependency import get_user -from apps.dependency.user import verify_user +from apps.dependency import verify_personal_token, verify_session from apps.schemas.request_data import PutFlowReq from apps.schemas.response_data import ( FlowStructureDeleteMsg, @@ -28,67 +27,57 @@ router = APIRouter( prefix="/api/flow", tags=["flow"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) -@router.get( - "/service", - responses={ +@router.get("/service", responses={ status.HTTP_200_OK: {"model": NodeServiceListRsp}, status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) -async def get_services( - user_sub: Annotated[str, Depends(get_user)], -) -> NodeServiceListRsp: +async def get_services(request: Request) -> NodeServiceListRsp: """获取用户可访问的节点元数据所在服务的信息""" - services = await FlowManager.get_service_by_user_id(user_sub) + services = await FlowManager.get_service_by_user_id(request.state.user_sub) if services is None: return NodeServiceListRsp( code=status.HTTP_404_NOT_FOUND, - message="未找到符合条件的服务", + message="未找到符合条件的Service", result=NodeServiceListMsg(), ) return NodeServiceListRsp( code=status.HTTP_200_OK, - message="节点元数据所在服务信息获取成功", + message="Node所在Service获取成功", result=NodeServiceListMsg(services=services), ) -@router.get( - "", - response_model=FlowStructureGetRsp, - responses={ +@router.get("", response_model=FlowStructureGetRsp, responses={ status.HTTP_403_FORBIDDEN: {"model": ResponseData}, status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) -async def get_flow( - user_sub: Annotated[str, Depends(get_user)], - app_id: Annotated[str, Query(alias="appId")], - flow_id: Annotated[str, Query(alias="flowId")], -) -> JSONResponse: +async def get_flow(request: Request, appId: str, flowId: str) -> JSONResponse: # noqa: N803 """获取流拓扑结构""" - if not await AppCenterManager.validate_user_app_access(user_sub, app_id): + if not await AppCenterManager.validate_user_app_access(request.state.user_sub, appId): return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=FlowStructureGetRsp( code=status.HTTP_403_FORBIDDEN, - message="用户没有权限访问该流", + message="用户没有权限访问该Workflow", result=FlowStructureGetMsg(), ).model_dump(exclude_none=True, by_alias=True), ) - result = await FlowManager.get_flow_by_app_and_flow_id(app_id, flow_id) + result = await FlowManager.get_flow_by_app_and_flow_id(appId, flowId) if result is None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content=FlowStructureGetRsp( code=status.HTTP_404_NOT_FOUND, - message="应用下流程获取失败", + message="应用的Workflow获取失败", result=FlowStructureGetMsg(), ).model_dump(exclude_none=True, by_alias=True), ) @@ -96,16 +85,13 @@ async def get_flow( status_code=status.HTTP_200_OK, content=FlowStructureGetRsp( code=status.HTTP_200_OK, - message="应用下流程获取成功", + message="应用的Workflow获取成功", result=FlowStructureGetMsg(flow=result), ).model_dump(exclude_none=True, by_alias=True), ) -@router.put( - "", - response_model=FlowStructurePutRsp, - responses={ +@router.put("", response_model=FlowStructurePutRsp, responses={ status.HTTP_400_BAD_REQUEST: {"model": ResponseData}, status.HTTP_403_FORBIDDEN: {"model": ResponseData}, status.HTTP_404_NOT_FOUND: {"model": ResponseData}, @@ -113,13 +99,13 @@ async def get_flow( }, ) async def put_flow( - user_sub: Annotated[str, Depends(get_user)], - app_id: Annotated[str, Query(alias="appId")], - flow_id: Annotated[str, Query(alias="flowId")], - put_body: Annotated[PutFlowReq, Body(...)], + request: Request, + appId: Annotated[str, Query()], # noqa: N803 + flowId: Annotated[str, Query()], # noqa: N803 + put_body: Annotated[PutFlowReq, Body()], ) -> JSONResponse: """修改流拓扑结构""" - if not await AppCenterManager.validate_app_belong_to_user(user_sub, app_id): + if not await AppCenterManager.validate_app_belong_to_user(request.state.user_sub, appId): return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=FlowStructurePutRsp( @@ -131,7 +117,7 @@ async def put_flow( put_body.flow = await FlowService.remove_excess_structure_from_flow(put_body.flow) await FlowService.validate_flow_illegal(put_body.flow) put_body.flow.connectivity = await FlowService.validate_flow_connectivity(put_body.flow) - result = await FlowManager.put_flow_by_app_and_flow_id(app_id, flow_id, put_body.flow) + result = await FlowManager.put_flow_by_app_and_flow_id(appId, flowId, put_body.flow) if result is None: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -141,8 +127,8 @@ async def put_flow( result=FlowStructurePutMsg(), ).model_dump(exclude_none=True, by_alias=True), ) - flow = await FlowManager.get_flow_by_app_and_flow_id(app_id, flow_id) - await AppCenterManager.update_app_publish_status(app_id, user_sub) + flow = await FlowManager.get_flow_by_app_and_flow_id(appId, flowId) + await AppCenterManager.update_app_publish_status(appId, request.state.user_sub) if flow is None: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, @@ -162,20 +148,13 @@ async def put_flow( ) -@router.delete( - "", - response_model=FlowStructureDeleteRsp, - responses={ +@router.delete("", response_model=FlowStructureDeleteRsp, responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) -async def delete_flow( - user_sub: Annotated[str, Depends(get_user)], - app_id: Annotated[str, Query(alias="appId")], - flow_id: Annotated[str, Query(alias="flowId")], -) -> JSONResponse: +async def delete_flow(request: Request, appId: str, flowId: str) -> JSONResponse: # noqa: N803 """删除流拓扑结构""" - if not await AppCenterManager.validate_app_belong_to_user(user_sub, app_id): + if not await AppCenterManager.validate_app_belong_to_user(request.state.user_sub, appId): return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=FlowStructureDeleteRsp( @@ -184,7 +163,7 @@ async def delete_flow( result=FlowStructureDeleteMsg(), ).model_dump(exclude_none=True, by_alias=True), ) - result = await FlowManager.delete_flow_by_app_and_flow_id(app_id, flow_id) + result = await FlowManager.delete_flow_by_app_and_flow_id(appId, flowId) if result is None: return JSONResponse( status_code=status.HTTP_404_NOT_FOUND, diff --git a/apps/routers/llm.py b/apps/routers/llm.py index b4f900648..78ea54c79 100644 --- a/apps/routers/llm.py +++ b/apps/routers/llm.py @@ -36,7 +36,7 @@ admin_router = APIRouter( ) -@router.get("/provider", response_model=ListLLMProviderRsp, responses={ +@admin_router.get("/provider", response_model=ListLLMProviderRsp, responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) @@ -73,7 +73,7 @@ async def list_llm( ) -@router.put("", responses={ +@admin_router.put("", responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, }, ) @@ -94,7 +94,7 @@ async def create_llm( ) -@router.delete( +@admin_router.delete( "", responses={ status.HTTP_404_NOT_FOUND: {"model": ResponseData}, @@ -127,7 +127,7 @@ async def update_user_llm( conversation_id: Annotated[str, Query(description="对话ID", alias="conversationId")], llm_id: Annotated[str, Query(description="llm ID", alias="llmId")] = "empty", ) -> JSONResponse: - """更新对话的知识库""" + """更新用户所选的大模型""" llm_id = await LLMManager.update_user_llm(request.state.user_sub, conversation_id, llm_id) return JSONResponse( status_code=status.HTTP_200_OK, diff --git a/apps/routers/mcp_service.py b/apps/routers/mcp_service.py index 7a110b8d6..cb96e8e85 100644 --- a/apps/routers/mcp_service.py +++ b/apps/routers/mcp_service.py @@ -5,10 +5,10 @@ import json import logging from typing import Annotated -from fastapi import APIRouter, Depends, File, HTTPException, Path, Query, UploadFile, status +from fastapi import APIRouter, Depends, File, HTTPException, Path, Query, Request, UploadFile, status from fastapi.responses import JSONResponse -from apps.dependency.user import get_user, verify_user +from apps.dependency.user import verify_admin, verify_personal_token, verify_session from apps.schemas.enum_var import SearchType from apps.schemas.request_data import ActiveMCPServiceRequest, UpdateMCPServiceRequest from apps.schemas.response_data import ( @@ -27,21 +27,25 @@ from apps.schemas.response_data import ( UploadMCPServiceIconRsp, ) from apps.services.mcp_service import MCPServiceManager -from apps.services.user import UserManager logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/mcp", tags=["mcp-service"], - dependencies=[Depends(verify_user)], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], +) +admin_router = APIRouter( + prefix="/api/admin/mcp", + tags=["mcp-service"], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + Depends(verify_admin), + ], ) - -async def _check_user_admin(user_sub: str) -> None: - user = await UserManager.get_user(user_sub) - if not user: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户未登录") - if not user.isAdmin: - raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="非管理员无法访问") @router.get("", response_model=GetMCPServiceListRsp | ResponseData) @@ -86,17 +90,15 @@ async def get_mcpservice_list( ) -@router.post("", response_model=UpdateMCPServiceRsp) +@admin_router.post("", response_model=UpdateMCPServiceRsp) async def create_or_update_mcpservice( - user_sub: Annotated[str, Depends(get_user)], # TODO: get_user直接获取所有用户信息 - data: UpdateMCPServiceRequest, + request: Request, + data: UpdateMCPServiceRequest, ) -> JSONResponse: """新建或更新MCP服务""" - await _check_user_admin(user_sub) - if not data.service_id: try: - service_id = await MCPServiceManager.create_mcpservice(data, user_sub) + service_id = await MCPServiceManager.create_mcpservice(data, request.state.user_sub) except Exception as e: logger.exception("[MCPServiceCenter] MCP服务创建失败") return JSONResponse( @@ -109,7 +111,7 @@ async def create_or_update_mcpservice( ) else: try: - service_id = await MCPServiceManager.update_mcpservice(data, user_sub) + service_id = await MCPServiceManager.update_mcpservice(data, request.state.user_sub) except Exception as e: logger.exception("[MCPService] 更新MCP服务失败") return JSONResponse( @@ -132,10 +134,10 @@ async def create_or_update_mcpservice( @router.get("/{serviceId}", response_model=GetMCPServiceDetailRsp) async def get_service_detail( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], - *, - edit: Annotated[bool, Query(..., description="是否为编辑模式")] = False, + request: Request, + service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], + *, + edit: Annotated[bool, Query(..., description="是否为编辑模式")] = False, ) -> JSONResponse: """获取MCP服务详情""" # 检查用户权限 @@ -194,16 +196,11 @@ async def get_service_detail( ) -@router.delete("/{serviceId}", response_model=DeleteMCPServiceRsp) -async def delete_service( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], -) -> JSONResponse: +@admin_router.delete("/{serviceId}", response_model=DeleteMCPServiceRsp) +async def delete_service(serviceId: Annotated[str, Path()]) -> JSONResponse: # noqa: N803 """删除服务""" - await _check_user_admin(user_sub) - try: - await MCPServiceManager.delete_mcpservice(service_id) + await MCPServiceManager.delete_mcpservice(serviceId) except Exception as e: err = f"[MCPServiceManager] 删除MCP服务失败: {e}" logger.exception(err) @@ -220,23 +217,20 @@ async def delete_service( content=DeleteMCPServiceRsp( code=status.HTTP_200_OK, message="OK", - result=BaseMCPServiceOperationMsg(serviceId=service_id), + result=BaseMCPServiceOperationMsg(serviceId=serviceId), ).model_dump(exclude_none=True, by_alias=True), ) -@router.post("/icon", response_model=UpdateMCPServiceRsp) +@admin_router.post("/icon", response_model=UpdateMCPServiceRsp) async def update_mcp_icon( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], - icon: Annotated[UploadFile, File(..., description="图标文件")], + serviceId: Annotated[str, Path()], # noqa: N803 + icon: Annotated[UploadFile, File(..., description="图标文件")], ) -> JSONResponse: """更新MCP服务图标""" - await _check_user_admin(user_sub) - # 检查当前MCP是否存在 try: - await MCPServiceManager.get_mcp_service(service_id) + await MCPServiceManager.get_mcp_service(serviceId) except Exception as e: raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"MCP服务未找到: {e!s}") from e @@ -251,7 +245,7 @@ async def update_mcp_icon( ).model_dump(exclude_none=True, by_alias=True), ) try: - url = await MCPServiceManager.save_mcp_icon(service_id, icon) + url = await MCPServiceManager.save_mcp_icon(serviceId, icon) except Exception as e: err = f"[MCPServiceManager] 更新MCP服务图标失败: {e}" logger.exception(err) @@ -268,23 +262,23 @@ async def update_mcp_icon( content=UploadMCPServiceIconRsp( code=status.HTTP_200_OK, message="OK", - result=UploadMCPServiceIconMsg(serviceId=service_id, url=url), + result=UploadMCPServiceIconMsg(serviceId=serviceId, url=url), ).model_dump(exclude_none=True, by_alias=True), ) @router.post("/{serviceId}", response_model=ActiveMCPServiceRsp) async def active_or_deactivate_mcp_service( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], - data: ActiveMCPServiceRequest, + request: Request, + serviceId: Annotated[str, Path()], # noqa: N803 + data: ActiveMCPServiceRequest, ) -> JSONResponse: """激活/取消激活mcp""" try: if data.active: - await MCPServiceManager.active_mcpservice(user_sub, service_id) + await MCPServiceManager.active_mcpservice(request.state.user_sub, serviceId) else: - await MCPServiceManager.deactive_mcpservice(user_sub, service_id) + await MCPServiceManager.deactive_mcpservice(request.state.user_sub, serviceId) except Exception as e: err = f"[MCPService] 激活mcp服务失败: {e!s}" if data.active else f"[MCPService] 取消激活mcp服务失败: {e!s}" logger.exception(err) @@ -301,6 +295,6 @@ async def active_or_deactivate_mcp_service( content=ActiveMCPServiceRsp( code=status.HTTP_200_OK, message="OK", - result=BaseMCPServiceOperationMsg(serviceId=service_id), + result=BaseMCPServiceOperationMsg(serviceId=serviceId), ).model_dump(exclude_none=True, by_alias=True), ) diff --git a/apps/routers/record.py b/apps/routers/record.py index 72f277be2..2a2fd591a 100644 --- a/apps/routers/record.py +++ b/apps/routers/record.py @@ -2,13 +2,14 @@ """FastAPI Record相关接口""" import json +import uuid from typing import Annotated -from fastapi import APIRouter, Depends, status +from fastapi import APIRouter, Depends, Path, Request, status from fastapi.responses import JSONResponse from apps.common.security import Security -from apps.dependency import get_user, verify_user +from apps.dependency import verify_admin, verify_personal_token, verify_session from apps.schemas.record import ( RecordContent, RecordData, @@ -31,19 +32,21 @@ router = APIRouter( prefix="/api/record", tags=["record"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) -@router.get( - "/{conversation_id}", - response_model=RecordListRsp, - responses={status.HTTP_403_FORBIDDEN: {"model": ResponseData}}, +@router.get("/{conversationId}", response_model=RecordListRsp, responses={ + status.HTTP_403_FORBIDDEN: {"model": ResponseData}, + }, ) -async def get_record(conversation_id: str, user_sub: Annotated[str, Depends(get_user)]) -> JSONResponse: +async def get_record(request: Request, conversationId: Annotated[uuid.UUID, Path()]) -> JSONResponse: # noqa: N803 """获取某个对话的所有问答对""" - cur_conv = await ConversationManager.get_conversation_by_conversation_id(user_sub, conversation_id) + cur_conv = await ConversationManager.get_conversation_by_conversation_id( + request.state.user_sub, conversationId, + ) # 判断conversation是否合法 if not cur_conv: return JSONResponse( @@ -55,7 +58,7 @@ async def get_record(conversation_id: str, user_sub: Annotated[str, Depends(get_ ).model_dump(exclude_none=True), ) - record_group_list = await RecordManager.query_record_group_by_conversation_id(conversation_id) + record_group_list = await RecordManager.query_record_group_by_conversation_id(conversationId) result = [] for record_group in record_group_list: for record in record_group.records: @@ -66,7 +69,7 @@ async def get_record(conversation_id: str, user_sub: Annotated[str, Depends(get_ id=record.id, groupId=record_group.id, taskId=record_group.task_id, - conversationId=conversation_id, + conversationId=conversationId, content=record_data, metadata=record.metadata if record.metadata diff --git a/apps/routers/service.py b/apps/routers/service.py index 2fc5fddca..ba90cee7e 100644 --- a/apps/routers/service.py +++ b/apps/routers/service.py @@ -4,7 +4,7 @@ import logging from typing import Annotated -from fastapi import APIRouter, Body, Depends, Path, Request, status +from fastapi import APIRouter, Depends, Path, Request, status from fastapi.responses import JSONResponse from apps.dependency.user import verify_personal_token, verify_session @@ -181,7 +181,7 @@ async def update_service(request: Request, data: UpdateServiceRequest) -> JSONRe @router.get("/{serviceId}", response_model=GetServiceDetailRsp) async def get_service_detail( - request: Request, serviceId: str, # noqa: N803 + request: Request, serviceId: Annotated[str, Path()], # noqa: N803 *, edit: bool = False, ) -> JSONResponse: """获取服务详情""" @@ -246,7 +246,7 @@ async def get_service_detail( @router.delete("/{serviceId}", response_model=DeleteServiceRsp) -async def delete_service(request: Request, serviceId: str) -> JSONResponse: # noqa: N803 +async def delete_service(request: Request, serviceId: Annotated[str, Path()]) -> JSONResponse: # noqa: N803 """删除服务""" try: await ServiceCenterManager.delete_service(request.state.user_sub, serviceId) @@ -285,13 +285,17 @@ async def delete_service(request: Request, serviceId: str) -> JSONResponse: # n @router.put("/{serviceId}", response_model=ModFavServiceRsp) async def modify_favorite_service( - user_sub: Annotated[str, Depends(get_user)], - service_id: Annotated[str, Path(..., alias="serviceId", description="服务ID")], - data: Annotated[ModFavServiceRequest, Body(..., description="更改收藏状态请求对象")], + request: Request, + serviceId: Annotated[str, Path()], # noqa: N803 + data: ModFavServiceRequest, ) -> JSONResponse: """修改服务收藏状态""" try: - success = await ServiceCenterManager.modify_favorite_service(user_sub, service_id, favorited=data.favorited) + success = await ServiceCenterManager.modify_favorite_service( + request.state.user_sub, + serviceId, + favorited=data.favorited, + ) if not success: return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, @@ -320,6 +324,6 @@ async def modify_favorite_service( result={}, ).model_dump(exclude_none=True, by_alias=True), ) - msg = ModFavServiceMsg(serviceId=service_id, favorited=data.favorited) + msg = ModFavServiceMsg(serviceId=serviceId, favorited=data.favorited) rsp = ModFavServiceRsp(code=status.HTTP_200_OK, message="OK", result=msg) return JSONResponse(status_code=status.HTTP_200_OK, content=rsp.model_dump(exclude_none=True, by_alias=True)) diff --git a/apps/routers/tag.py b/apps/routers/tag.py index 521c5b5aa..7d5553113 100644 --- a/apps/routers/tag.py +++ b/apps/routers/tag.py @@ -10,7 +10,7 @@ from apps.schemas.response_data import ResponseData from apps.services.tag import TagManager admin_router = APIRouter( - prefix="/api/tag", + prefix="/api/admin/tag", tags=["tag"], dependencies=[ Depends(verify_session), -- Gitee From ea3fab6f4303aa233cf2e71811dd5722c7fa18a5 Mon Sep 17 00:00:00 2001 From: z30057876 Date: Thu, 24 Jul 2025 11:39:26 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/routers/conversation.py | 78 ++++++++++++------------------------ apps/routers/mcp_service.py | 12 +++++- 2 files changed, 37 insertions(+), 53 deletions(-) diff --git a/apps/routers/conversation.py b/apps/routers/conversation.py index 23c36b62d..df1a30764 100644 --- a/apps/routers/conversation.py +++ b/apps/routers/conversation.py @@ -2,14 +2,15 @@ """FastAPI:对话相关接口""" import logging +import uuid from datetime import datetime from typing import Annotated import pytz -from fastapi import APIRouter, Body, Depends, Query, status +from fastapi import APIRouter, Body, Depends, Query, Request, status from fastapi.responses import JSONResponse -from apps.dependency import verify_admin, verify_personal_token, verify_session +from apps.dependency import verify_personal_token, verify_session from apps.models.conversation import Conversation from apps.schemas.request_data import ( DeleteConversationData, @@ -23,8 +24,6 @@ from apps.schemas.response_data import ( ConversationListRsp, DeleteConversationMsg, DeleteConversationRsp, - KbIteam, - LLMIteam, ResponseData, UpdateConversationRsp, ) @@ -36,17 +35,15 @@ router = APIRouter( prefix="/api/conversation", tags=["conversation"], dependencies=[ - Depends(verify_user), + Depends(verify_session), + Depends(verify_personal_token), ], ) logger = logging.getLogger(__name__) async def create_new_conversation( - user_sub: str, - app_id: str = "", - llm_id: str = "empty", - kb_ids: list[str] | None = None, + user_sub: str, app_id: uuid.UUID | None = None, *, debug: bool = False, ) -> Conversation: @@ -58,8 +55,6 @@ async def create_new_conversation( new_conv = await ConversationManager.add_conversation_by_user_sub( user_sub, app_id=app_id, - llm_id=llm_id, - kb_ids=kb_ids or [], debug=debug, ) if not new_conv: @@ -67,49 +62,26 @@ async def create_new_conversation( raise RuntimeError(err) return new_conv -@router.get( - "", - response_model=ConversationListRsp, - responses={ +@router.get("", response_model=ConversationListRsp, responses={ status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": ResponseData}, }, ) -async def get_conversation_list(user_sub: Annotated[str, Depends(get_user)]) -> JSONResponse: +async def get_conversation_list(request: Request) -> JSONResponse: """获取对话列表""" - conversations = await ConversationManager.get_conversation_by_user_sub(user_sub) + conversations = await ConversationManager.get_conversation_by_user_sub( + request.state.user_sub, + ) # 把已有对话转换为列表 result_conversations = [] for conv in conversations: conversation_list_item = ConversationListItem( conversationId=conv.id, title=conv.title, - docCount=await DocumentManager.get_doc_count(user_sub, conv.id), - createdTime=datetime.fromtimestamp(conv.createdAt, tz=pytz.timezone("Asia/Shanghai")).strftime( - "%Y-%m-%d %H:%M:%S", - ), - appId=conv.appId if conv.appId else "", - debug=conv.debug if conv.debug else False, + docCount=await DocumentManager.get_doc_count(conv.id), + createdTime=conv.createdAt.strftime("%Y-%m-%d %H:%M:%S"), + appId=conv.appId, + debug=conv.isTemporary, ) - if conv.llm: - llm_item = LLMIteam( - llmId=conv.llm.llm_id, - modelName=conv.llm.model_name, - icon=conv.llm.icon, - ) - else: - llm_item = None - if conv.kb_list: - kb_item_list = [] - for kb in conv.kb_list: - kb_item = KbIteam( - kbId=kb.kb_id, - kbName=kb.kb_name, - ) - kb_item_list.append(kb_item) - else: - kb_item_list = [] - conversation_list_item.llm = llm_item - conversation_list_item.kb_list = kb_item_list result_conversations.append(conversation_list_item) return JSONResponse( @@ -124,10 +96,8 @@ async def get_conversation_list(user_sub: Annotated[str, Depends(get_user)]) -> @router.post("", response_model=AddConversationRsp) async def add_conversation( - user_sub: Annotated[str, Depends(get_user)], + request: Request, app_id: Annotated[str, Query(..., alias="appId")] = "", - llm_id: Annotated[str, Body(..., alias="llmId")] = "empty", - kb_ids: Annotated[list[str] | None, Body(..., alias="kbIds")] = None, *, debug: Annotated[bool, Query()] = False, ) -> JSONResponse: @@ -137,10 +107,8 @@ async def add_conversation( app_id = app_id if app_id else "" debug = debug if debug is not None else False new_conv = await create_new_conversation( - user_sub, + request.state.user_sub, app_id=app_id, - llm_id=llm_id, - kb_ids=kb_ids or [], debug=debug, ) except RuntimeError as e: @@ -223,16 +191,22 @@ async def update_conversation( @router.delete("", response_model=ResponseData) async def delete_conversation( + request: Request, post_body: DeleteConversationData, - user_sub: Annotated[str, Depends(get_user)], ) -> JSONResponse: """删除特定对话""" deleted_conversation = [] for conversation_id in post_body.conversation_list: # 删除对话 - await ConversationManager.delete_conversation_by_conversation_id(user_sub, conversation_id) + await ConversationManager.delete_conversation_by_conversation_id( + request.state.user_sub, + conversation_id, + ) # 删除对话对应的文件 - await DocumentManager.delete_document_by_conversation_id(conversation_id) + await DocumentManager.delete_document_by_conversation_id( + request.state.user_sub, + conversation_id, + ) deleted_conversation.append(conversation_id) return JSONResponse( diff --git a/apps/routers/mcp_service.py b/apps/routers/mcp_service.py index cb96e8e85..397d90444 100644 --- a/apps/routers/mcp_service.py +++ b/apps/routers/mcp_service.py @@ -196,6 +196,16 @@ async def get_service_detail( ) +@admin_router.get("/{serviceId}", response_model=GetMCPServiceDetailRsp) +async def get_service_detail(serviceId: Annotated[str, Path()]) -> JSONResponse: # noqa: N803 + """获取MCP服务详情""" + try: + data = await MCPServiceManager.get_mcp_service(serviceId) + config, icon = await MCPServiceManager.get_mcp_config(serviceId) + except Exception as e: + pass + + @admin_router.delete("/{serviceId}", response_model=DeleteMCPServiceRsp) async def delete_service(serviceId: Annotated[str, Path()]) -> JSONResponse: # noqa: N803 """删除服务""" @@ -225,7 +235,7 @@ async def delete_service(serviceId: Annotated[str, Path()]) -> JSONResponse: # @admin_router.post("/icon", response_model=UpdateMCPServiceRsp) async def update_mcp_icon( serviceId: Annotated[str, Path()], # noqa: N803 - icon: Annotated[UploadFile, File(..., description="图标文件")], + icon: UploadFile, ) -> JSONResponse: """更新MCP服务图标""" # 检查当前MCP是否存在 -- Gitee From 9a9e2997feae0795c50c24d5570b0a97f583731a Mon Sep 17 00:00:00 2001 From: z30057876 Date: Thu, 24 Jul 2025 14:46:03 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=9B=9E=E5=90=88=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=89=88=E6=9C=AC=E5=8F=B7=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/routers/appcenter.py | 36 +++++++++---------- apps/schemas/response_data.py | 2 +- deploy/chart/authhub/Chart.yaml | 4 +-- deploy/chart/databases/Chart.yaml | 4 +-- deploy/chart/euler_copilot/Chart.yaml | 4 +-- deploy/chart/euler_copilot/values.yaml | 18 +++++----- .../scripts/2-install-tools/install_tools.sh | 2 +- deploy/scripts/9-other-script/save_images.sh | 4 +-- 8 files changed, 36 insertions(+), 38 deletions(-) diff --git a/apps/routers/appcenter.py b/apps/routers/appcenter.py index 3230e4dc2..c66eb5bc1 100644 --- a/apps/routers/appcenter.py +++ b/apps/routers/appcenter.py @@ -7,7 +7,7 @@ from typing import Annotated from fastapi import APIRouter, Body, Depends, Path, Query, Request, status from fastapi.responses import JSONResponse -from apps.dependency.user import verify_session +from apps.dependency.user import verify_personal_token, verify_session from apps.exceptions import InstancePermissionError from apps.schemas.appcenter import AppFlowInfo, AppPermissionData from apps.schemas.enum_var import AppFilterType, AppType @@ -30,7 +30,10 @@ logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/app", tags=["appcenter"], - dependencies=[Depends(verify_session)], + dependencies=[ + Depends(verify_session), + Depends(verify_personal_token), + ], ) @@ -38,15 +41,13 @@ router = APIRouter( async def get_applications( # noqa: PLR0913 request: Request, *, - my_app: Annotated[bool, Query(..., alias="createdByMe", description="筛选我创建的")] = False, - my_fav: Annotated[bool, Query(..., alias="favorited", description="筛选我收藏的")] = False, - keyword: Annotated[str | None, Query(..., alias="keyword", description="搜索关键字")] = None, - app_type: Annotated[AppType | None, Query(..., alias="appType", description="应用类型")] = None, - page: Annotated[int, Query(..., alias="page", ge=1, description="页码")] = 1, + createdByMe: bool = False, favorited: bool = False, # noqa: N803 + keyword: str | None = None, appType: AppType | None = None, # noqa: N803 + page: Annotated[int, Query(ge=1)] = 1, ) -> JSONResponse: """获取应用列表""" user_sub: str = request.state.user_sub - if my_app and my_fav: # 只能同时使用一个过滤条件 + if createdByMe and favorited: # 只能同时使用一个过滤条件 return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content=ResponseData( @@ -56,11 +57,13 @@ async def get_applications( # noqa: PLR0913 ).model_dump(exclude_none=True, by_alias=True), ) try: - filter_type = AppFilterType.USER if my_app else (AppFilterType.FAVORITE if my_fav else AppFilterType.ALL) + filter_type = ( + AppFilterType.USER if createdByMe else (AppFilterType.FAVORITE if favorited else AppFilterType.ALL) + ) app_cards, total_apps = await AppCenterManager.fetch_apps( user_sub, keyword, - app_type, + appType, page, filter_type, ) @@ -89,10 +92,7 @@ async def get_applications( # noqa: PLR0913 @router.post("", response_model=BaseAppOperationRsp | ResponseData) -async def create_or_update_application( - raw_request: Request, - request: Annotated[CreateAppRequest, Body(...)], -) -> JSONResponse: +async def create_or_update_application(raw_request: Request, request: CreateAppRequest) -> JSONResponse: """创建或更新应用""" user_sub: str = raw_request.state.user_sub @@ -156,7 +156,7 @@ async def create_or_update_application( @router.get("/recent", response_model=GetRecentAppListRsp | ResponseData) async def get_recently_used_applications( request: Request, - count: Annotated[int, Query(..., ge=1, le=10)] = 5, + count: Annotated[int, Query(ge=1, le=10)] = 5, ) -> JSONResponse: """获取最近使用的应用""" user_sub: str = request.state.user_sub @@ -183,12 +183,10 @@ async def get_recently_used_applications( @router.get("/{appId}", response_model=GetAppPropertyRsp | ResponseData) -async def get_application( - app_id: Annotated[str, Path(..., alias="appId", description="应用ID")], -) -> JSONResponse: +async def get_application(appId: Annotated[str, Path()]) -> JSONResponse: # noqa: N803 """获取应用详情""" try: - app_data = await AppCenterManager.fetch_app_data_by_id(app_id) + app_data = await AppCenterManager.fetch_app_data_by_id(appId) except ValueError: logger.exception("[AppCenter] 获取应用详情请求无效") return JSONResponse( diff --git a/apps/schemas/response_data.py b/apps/schemas/response_data.py index 5e04889d2..c305964ef 100644 --- a/apps/schemas/response_data.py +++ b/apps/schemas/response_data.py @@ -91,7 +91,7 @@ class KbIteam(BaseModel): class ConversationListItem(BaseModel): """GET /api/conversation Result数据结构""" - conversation_id: str = Field(alias="conversationId") + conversation_id: uuid.UUID = Field(alias="conversationId") title: str doc_count: int = Field(alias="docCount") created_time: str = Field(alias="createdTime") diff --git a/deploy/chart/authhub/Chart.yaml b/deploy/chart/authhub/Chart.yaml index 79a92abd0..c1029a168 100644 --- a/deploy/chart/authhub/Chart.yaml +++ b/deploy/chart/authhub/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: authhub-chart description: AuthHub Helm部署包 type: application -version: 0.9.5 -appVersion: "0.9.5" +version: 0.9.6 +appVersion: "0.9.6" diff --git a/deploy/chart/databases/Chart.yaml b/deploy/chart/databases/Chart.yaml index acde29003..f75228d53 100644 --- a/deploy/chart/databases/Chart.yaml +++ b/deploy/chart/databases/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: euler-copilot-databases description: Euler Copilot 数据库 Helm部署包 type: application -version: 0.9.5 -appVersion: "0.9.5" +version: 0.9.6 +appVersion: "0.9.6" diff --git a/deploy/chart/euler_copilot/Chart.yaml b/deploy/chart/euler_copilot/Chart.yaml index d688d6ef0..9d9ffecff 100644 --- a/deploy/chart/euler_copilot/Chart.yaml +++ b/deploy/chart/euler_copilot/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: euler-copilot description: Euler Copilot Helm部署包 type: application -version: 0.9.5 -appVersion: "0.9.5" +version: 0.9.6 +appVersion: "0.9.6" diff --git a/deploy/chart/euler_copilot/values.yaml b/deploy/chart/euler_copilot/values.yaml index 6944f1bfe..64566314a 100644 --- a/deploy/chart/euler_copilot/values.yaml +++ b/deploy/chart/euler_copilot/values.yaml @@ -25,7 +25,7 @@ models: # 用于Function Call的模型;建议使用特定推理框架 functionCall: # 推理框架类型,默认为ollama - # 可用的框架类型:["vllm", "sglang", "ollama", "openai"] + # 可用的框架类型:["vllm", "ollama", "function_call", "json_mode", "structured_output"] backend: # [必填] 模型地址;请根据 API 提供商文档确定是否需要带上“v1”后缀 # 选择不填则与问答模型一致 @@ -89,8 +89,8 @@ euler_copilot: framework: # [必填] 是否部署Framework后端框架服务 enabled: true - # 镜像设置;默认为hub.oepkgs.net/neocopilot/euler-copilot-framework:0.9.5-x86 - # 镜像标签:["0.9.5-x86", "0.9.5-arm"] + # 镜像设置;默认为hub.oepkgs.net/neocopilot/euler-copilot-framework:0.9.6-x86 + # 镜像标签:["0.9.6-x86", "0.9.6-arm"] image: # 容器根目录只读 readOnly: @@ -106,8 +106,8 @@ euler_copilot: web: # [必填] 是否部署Web前端用户界面 enabled: true - # 镜像设置;默认为hub.oepkgs.net/neocopilot/euler-copilot-web:0.9.5-x86 - # 镜像标签:["0.9.5-x86", "0.9.5-arm"] + # 镜像设置;默认为hub.oepkgs.net/neocopilot/euler-copilot-web:0.9.6-x86 + # 镜像标签:["0.9.6-x86", "0.9.6-arm"] image: # 容器根目录只读 readOnly: @@ -123,8 +123,8 @@ euler_copilot: rag_web: # [必填] 是否部署RAG Web前端用户界面 enabled: true - # 镜像设置;默认为hub.oepkgs.net/neocopilot/data_chain_web:0.9.5-x86 - # 镜像标签:["0.9.5-x86", "0.9.5-arm"] + # 镜像设置;默认为hub.oepkgs.net/neocopilot/data_chain_web:0.9.6-x86 + # 镜像标签:["0.9.6-x86", "0.9.6-arm"] image: # 容器根目录只读 readOnly: @@ -140,8 +140,8 @@ euler_copilot: rag: # [必填] 是否部署RAG后端服务 enabled: true - # 镜像设置;默认为hub.oepkgs.net/neocopilot/data_chain_back_end:0.9.5-x86 - # 镜像标签:["0.9.5-x86", "0.9.5-arm"] + # 镜像设置;默认为hub.oepkgs.net/neocopilot/data_chain_back_end:0.9.6-x86 + # 镜像标签:["0.9.6-x86", "0.9.6-arm"] image: # 容器根目录只读 readOnly: diff --git a/deploy/scripts/2-install-tools/install_tools.sh b/deploy/scripts/2-install-tools/install_tools.sh index 3b19f58e1..804c735da 100755 --- a/deploy/scripts/2-install-tools/install_tools.sh +++ b/deploy/scripts/2-install-tools/install_tools.sh @@ -3,7 +3,7 @@ GITHUB_MIRROR="https://gh-proxy.com" ARCH=$(uname -m) TOOLS_DIR="/home/eulercopilot/tools" -eulercopilot_version=0.9.5 +eulercopilot_version=0.9.6 SCRIPT_PATH="$( cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 diff --git a/deploy/scripts/9-other-script/save_images.sh b/deploy/scripts/9-other-script/save_images.sh index c8f59cf76..edb06bc56 100755 --- a/deploy/scripts/9-other-script/save_images.sh +++ b/deploy/scripts/9-other-script/save_images.sh @@ -8,7 +8,7 @@ BLUE='\033[0;34m' NC='\033[0m' # 恢复默认颜色 # 默认配置 -eulercopilot_version="0.9.5" +eulercopilot_version="0.9.6" ARCH_SUFFIX="" OUTPUT_DIR="/home/eulercopilot/images/${eulercopilot_version}" @@ -23,7 +23,7 @@ show_help() { echo -e " --arch <架构> 指定系统架构 (arm/x86, 默认自动检测)" echo -e "" echo -e "${YELLOW}示例:${NC}" - echo -e " $0 --version 0.9.5 --arch arm" + echo -e " $0 --version 0.9.6 --arch arm" echo -e " $0 --help" exit 0 } -- Gitee