refactor(api): standardize responses and restore media config contracts

This commit is contained in:
jxxghp
2026-08-12 23:15:13 +08:00
parent 0ac641c05d
commit baf8850d63
100 changed files with 4422 additions and 1286 deletions

View File

@@ -5,13 +5,13 @@ from typing import Annotated, Any, Dict, List, Optional
import aiofiles
from anyio import Path as AsyncPath
from fastapi import APIRouter, Depends, Header, HTTPException, Security
from fastapi import Depends, Header, HTTPException, Security
from fastapi.concurrency import run_in_threadpool
from starlette import status
from starlette.responses import StreamingResponse
from app import schemas
from app.api.apiv2_utils import API_V2_STR, OPENAPI_V2_PATH
from app.api.response import ResponseAPIRouter
from app.command import Command
from app.core.cache import async_fresh
from app.core.config import settings
@@ -39,15 +39,13 @@ from app.schemas.types import ChainEventType, SystemConfigKey
PROTECTED_ROUTES = {
"/api/v1/openapi.json",
OPENAPI_V2_PATH,
"/docs",
"/docs/oauth2-redirect",
"/redoc",
}
PLUGIN_PREFIX = f"{settings.API_V1_STR}/plugin"
PLUGIN_V2_PREFIX = f"{API_V2_STR}/plugin"
router = APIRouter()
router = ResponseAPIRouter()
_plugin_release_refresh_tasks: set[asyncio.Task] = set()
@@ -166,11 +164,8 @@ def _update_plugin_api_routes(plugin_id: Optional[str], action: str):
elif Depends(verify_apikey) not in dependencies:
dependencies.append(Depends(verify_apikey))
app.add_api_route(**api, tags=["plugin"])
v2_api = api.copy()
v2_api["path"] = api_path.replace(PLUGIN_PREFIX, PLUGIN_V2_PREFIX, 1)
app.add_api_route(**v2_api, tags=["plugin"])
is_modified = True
logger.debug(f"Added plugin routes: {api_path}, {v2_api['path']}")
logger.debug(f"Added plugin route: {api_path}")
except Exception as e:
logger.error(f"Error adding plugin route {api_path}: {str(e)}")
@@ -188,12 +183,9 @@ def _remove_routes(plugin_id: str) -> bool:
"""
if not plugin_id:
return False
prefixes = {
f"{PLUGIN_PREFIX}/{plugin_id}/",
f"{PLUGIN_V2_PREFIX}/{plugin_id}/",
}
prefix = f"{PLUGIN_PREFIX}/{plugin_id}/"
routes_to_remove = [
route for route in app.routes if any(route.path.startswith(prefix) for prefix in prefixes)
route for route in app.routes if route.path.startswith(prefix)
]
removed = False
for route in routes_to_remove:
@@ -425,7 +417,11 @@ async def plugin_history(
return plugin
@router.get("/releases/{plugin_id}", summary="获取插件Release版本", response_model=dict)
@router.get(
"/releases/{plugin_id}",
summary="获取插件Release版本",
response_model=schemas.PluginReleaseData,
)
async def plugin_releases(
plugin_id: str,
_: User = Depends(get_current_active_superuser_async),
@@ -484,7 +480,11 @@ async def plugin_releases(
}
@router.get("/statistic", summary="插件安装统计", response_model=dict)
@router.get(
"/statistic",
summary="插件安装统计",
response_model=schemas.JsonObject,
)
async def statistic(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
"""
插件安装统计
@@ -495,7 +495,7 @@ async def statistic(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
@router.get(
"/rating",
summary="批量查询插件评分",
response_model=Dict[str, schemas.PluginRating],
response_model=schemas.PluginRatingMap,
)
async def plugin_ratings(
plugin_ids: Optional[str] = None,
@@ -531,7 +531,7 @@ async def plugin_rating(
@router.post(
"/rating/{plugin_id}",
summary="提交插件评分",
response_model=schemas.Response,
response_model=schemas.Response[schemas.PluginRating],
)
async def rate_plugin(
plugin_id: str,
@@ -558,7 +558,7 @@ async def rate_plugin(
@router.get(
"/reload/{plugin_id}", summary="重新加载插件", response_model=schemas.Response
"/reload/{plugin_id}", summary="重新加载插件", response_model=schemas.Response[None]
)
def reload_plugin(
plugin_id: str, _: User = Depends(get_current_active_superuser)
@@ -573,7 +573,7 @@ def reload_plugin(
return schemas.Response(success=True)
@router.get("/install/{plugin_id}", summary="安装插件", response_model=schemas.Response)
@router.get("/install/{plugin_id}", summary="安装插件", response_model=schemas.Response[None])
async def install(
plugin_id: str,
repo_url: Optional[str] = "",
@@ -623,7 +623,11 @@ async def install(
return schemas.Response(success=True)
@router.get("/remotes", summary="获取插件联邦组件列表", response_model=List[dict])
@router.get(
"/remotes",
summary="获取插件联邦组件列表",
response_model=List[schemas.PluginRemoteInfo],
)
async def remotes(token: str) -> Any:
"""
获取插件联邦组件列表
@@ -645,7 +649,11 @@ def plugin_sidebar_nav(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
return PluginManager().get_plugin_sidebar_nav()
@router.get("/form/{plugin_id}", summary="获取插件表单页面")
@router.get(
"/form/{plugin_id}",
summary="获取插件表单页面",
response_model=schemas.JsonObject,
)
def plugin_form(
plugin_id: str, _: User = Depends(get_current_active_superuser)
) -> dict:
@@ -677,7 +685,11 @@ def plugin_form(
return {}
@router.get("/page/{plugin_id}", summary="获取插件数据页面")
@router.get(
"/page/{plugin_id}",
summary="获取插件数据页面",
response_model=schemas.JsonObject,
)
def plugin_page(
plugin_id: str, _: User = Depends(get_current_active_superuser)
) -> dict:
@@ -701,7 +713,11 @@ def plugin_page(
return {}
@router.get("/dashboard/meta", summary="获取所有插件仪表板元信息")
@router.get(
"/dashboard/meta",
summary="获取所有插件仪表板元信息",
response_model=List[schemas.PluginDashboardMetaItem],
)
def plugin_dashboard_meta(
_: User = Depends(get_current_active_superuser),
) -> List[dict]:
@@ -737,7 +753,7 @@ def plugin_dashboard(
@router.get(
"/reset/{plugin_id}", summary="重置插件配置及数据", response_model=schemas.Response
"/reset/{plugin_id}", summary="重置插件配置及数据", response_model=schemas.Response[None]
)
def reset_plugin(
plugin_id: str, _: User = Depends(get_current_active_superuser)
@@ -761,7 +777,24 @@ def reset_plugin(
return schemas.Response(success=True)
@router.get("/file/{plugin_id}/{filepath:path}", summary="获取插件静态文件")
@router.get(
"/file/{plugin_id}/{filepath:path}",
summary="获取插件静态文件",
response_model=None,
response_class=StreamingResponse,
responses={
200: {
"description": "插件静态资源",
"content": {
"application/octet-stream": {
"schema": {"type": "string", "format": "binary"}
},
"application/javascript": {"schema": {"type": "string"}},
"text/css": {"schema": {"type": "string"}},
},
}
},
)
async def plugin_static_file(
plugin_id: str,
filepath: str,
@@ -839,7 +872,11 @@ async def plugin_static_file(
raise HTTPException(status_code=500, detail="Internal Server Error")
@router.get("/folders", summary="获取插件文件夹配置", response_model=dict)
@router.get(
"/folders",
summary="获取插件文件夹配置",
response_model=schemas.PluginFoldersData,
)
async def get_plugin_folders(
_: User = Depends(get_current_active_superuser_async),
) -> dict:
@@ -854,7 +891,7 @@ async def get_plugin_folders(
return {}
@router.post("/folders", summary="保存插件文件夹配置", response_model=schemas.Response)
@router.post("/folders", summary="保存插件文件夹配置", response_model=schemas.Response[None])
async def save_plugin_folders(
folders: dict, _: User = Depends(get_current_active_superuser_async)
) -> Any:
@@ -870,7 +907,7 @@ async def save_plugin_folders(
@router.post(
"/folders/{folder_name}", summary="创建插件文件夹", response_model=schemas.Response
"/folders/{folder_name}", summary="创建插件文件夹", response_model=schemas.Response[None]
)
async def create_plugin_folder(
folder_name: str, _: User = Depends(get_current_active_superuser_async)
@@ -890,7 +927,7 @@ async def create_plugin_folder(
@router.delete(
"/folders/{folder_name}", summary="删除插件文件夹", response_model=schemas.Response
"/folders/{folder_name}", summary="删除插件文件夹", response_model=schemas.Response[None]
)
async def delete_plugin_folder(
folder_name: str, _: User = Depends(get_current_active_superuser_async)
@@ -912,7 +949,7 @@ async def delete_plugin_folder(
@router.put(
"/folders/{folder_name}/plugins",
summary="更新文件夹中的插件",
response_model=schemas.Response,
response_model=schemas.Response[None],
)
async def update_folder_plugins(
folder_name: str,
@@ -931,7 +968,7 @@ async def update_folder_plugins(
@router.post(
"/clone/{plugin_id}", summary="创建插件分身", response_model=schemas.Response
"/clone/{plugin_id}", summary="创建插件分身", response_model=schemas.Response[None]
)
def clone_plugin(
plugin_id: str, clone_data: dict, _: User = Depends(get_current_active_superuser)
@@ -962,7 +999,11 @@ def clone_plugin(
return schemas.Response(success=False, message=f"创建插件分身失败:{str(e)}")
@router.get("/{plugin_id}", summary="获取插件配置")
@router.get(
"/{plugin_id}",
summary="获取插件配置",
response_model=schemas.JsonObject,
)
async def plugin_config(
plugin_id: str, _: User = Depends(get_current_active_superuser_async)
) -> dict:
@@ -972,7 +1013,7 @@ async def plugin_config(
return PluginManager().get_plugin_config(plugin_id)
@router.put("/{plugin_id}", summary="更新插件配置", response_model=schemas.Response)
@router.put("/{plugin_id}", summary="更新插件配置", response_model=schemas.Response[None])
def set_plugin_config(
plugin_id: str, conf: dict, _: User = Depends(get_current_active_superuser)
) -> Any:
@@ -989,7 +1030,7 @@ def set_plugin_config(
return schemas.Response(success=True)
@router.delete("/{plugin_id}", summary="卸载插件", response_model=schemas.Response)
@router.delete("/{plugin_id}", summary="卸载插件", response_model=schemas.Response[None])
def uninstall_plugin(
plugin_id: str, _: User = Depends(get_current_active_superuser)
) -> Any: