VWED_server/routes/dynamic_api.py

255 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
动态API路由处理器
处理脚本注册的动态API请求
"""
import time
import asyncio
from fastapi import APIRouter, Request
from services.script_registry_service import get_global_registry
from utils.logger import get_logger
from utils.api_response import success_response, error_response
logger = get_logger("routes.dynamic_api")
router = APIRouter()
@router.api_route("/api/dynamic/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
async def handle_dynamic_api(request: Request, path: str):
"""处理脚本注册的动态API请求"""
try:
registry = get_global_registry()
method = request.method
route_key = f"{method}:/{path}"
# 查找对应的路由处理器
route_info = registry.get_api_route(f"/{path}", method)
if not route_info:
return error_response(f"API路由未找到: {method} /{path}", code=404)
# 获取请求数据
request_data = {
"method": method,
"path": f"/{path}",
"query_params": dict(request.query_params),
"headers": dict(request.headers)
}
# 处理请求体数据
if method in ["POST", "PUT", "PATCH"]:
try:
body = await request.json()
request_data["body"] = body
except:
request_data["body"] = {}
else:
request_data["body"] = {}
# 获取处理器
handler = route_info["handler"]
start_time = time.time()
# 处理简化参数格式
params = route_info.get("params", {})
if params and method in ["POST", "PUT", "PATCH"]:
# 从请求体提取参数并传递给处理器(新的简化格式)
handler_args = {}
for param_name, default_value in params.items():
handler_args[param_name] = request_data["body"].get(param_name, default_value)
# 检测处理器是否为异步函数
if asyncio.iscoroutinefunction(handler):
result = await handler(**handler_args)
else:
result = handler(**handler_args)
else:
# 传递原始请求数据(兼容旧格式)
if asyncio.iscoroutinefunction(handler):
result = await handler(request_data)
else:
result = handler(request_data)
# 计算响应时间
response_time_ms = int((time.time() - start_time) * 1000)
# 更新调用统计
registry.update_api_call_stats(route_key, response_time_ms)
logger.info(f"动态API调用成功: {method} /{path} (脚本: {route_info['script_id']}, 耗时: {response_time_ms}ms)")
return success_response(
data=result,
message=f"API调用成功"
)
except Exception as e:
logger.error(f"动态API处理失败: {method} /{path} - {e}", exc_info=True)
return error_response(f"动态API处理失败: {str(e)}", code=500)
@router.get("/api/dynamic-registry/status")
async def get_dynamic_registry_status():
"""获取动态API注册状态"""
try:
registry = get_global_registry()
registrations = registry.get_all_registrations()
# 统计信息
stats = {
"total_apis": len(registrations["api_routes"]),
"total_functions": len(registrations["registered_functions"]),
"total_events": sum(len(listeners) for listeners in registrations["event_listeners"].values()),
"total_timers": len(registrations["timers"]),
"active_scripts": len(registrations["active_scripts"])
}
return success_response(
data={
"stats": stats,
"registrations": registrations
},
message="获取注册状态成功"
)
except Exception as e:
logger.error(f"获取动态注册状态失败: {e}", exc_info=True)
return error_response(f"获取注册状态失败: {str(e)}")
@router.get("/api/dynamic-registry/apis")
async def list_dynamic_apis():
"""列出所有动态注册的API"""
try:
registry = get_global_registry()
api_routes = registry.api_routes
# 格式化API列表
api_list = []
for route_key, route_info in api_routes.items():
api_list.append({
"route_key": route_key,
"path": route_info["path"],
"method": route_info["method"],
"script_id": route_info["script_id"],
"description": route_info["description"],
"call_count": route_info["call_count"],
"last_called_at": route_info["last_called_at"],
"average_response_time_ms": route_info["average_response_time_ms"],
"registered_at": route_info["registered_at"],
"params": route_info.get("params", {}) # 显示简化的参数格式
})
return success_response(
data={"apis": api_list, "total": len(api_list)},
message=f"获取动态API列表成功{len(api_list)}个接口"
)
except Exception as e:
logger.error(f"获取动态API列表失败: {e}", exc_info=True)
return error_response(f"获取API列表失败: {str(e)}")
@router.get("/api/dynamic-registry/functions")
async def list_dynamic_functions():
"""列出所有动态注册的函数"""
try:
registry = get_global_registry()
functions = registry.registered_functions
# 格式化函数列表
function_list = []
for function_name, function_info in functions.items():
function_list.append({
"name": function_name,
"script_id": function_info["script_id"],
"description": function_info["description"],
"is_async": function_info["is_async"],
"call_count": function_info["call_count"],
"success_count": function_info["success_count"],
"error_count": function_info["error_count"],
"last_called_at": function_info["last_called_at"],
"average_execution_time_ms": function_info["average_execution_time_ms"],
"registered_at": function_info["registered_at"],
"params": function_info.get("params", {}), # 显示简化的参数格式
"tags": function_info["tags"]
})
return success_response(
data={"functions": function_list, "total": len(function_list)},
message=f"获取动态函数列表成功,共{len(function_list)}个函数"
)
except Exception as e:
logger.error(f"获取动态函数列表失败: {e}", exc_info=True)
return error_response(f"获取函数列表失败: {str(e)}")
@router.post("/api/dynamic-registry/test-function/{function_name}")
async def test_dynamic_function(function_name: str, request: Request):
"""测试动态注册的函数"""
try:
registry = get_global_registry()
function_info = registry.get_function_info(function_name)
if not function_info:
return error_response(f"函数 {function_name} 未注册")
# 获取测试参数
try:
test_args = await request.json()
except:
test_args = {}
# 获取处理器
handler = function_info["handler"]
start_time = time.time()
# 处理简化参数格式
params = function_info.get("params", {})
if params:
# 使用简化的参数格式
handler_args = {}
for param_name, default_value in params.items():
handler_args[param_name] = test_args.get(param_name, default_value)
if function_info["is_async"]:
result = await handler(**handler_args)
else:
result = handler(**handler_args)
else:
# 兼容旧格式
if function_info["is_async"]:
result = await handler(test_args)
else:
result = handler(test_args)
# 计算执行时间
execution_time_ms = int((time.time() - start_time) * 1000)
# 更新统计
registry.update_function_call_stats(function_name, execution_time_ms, True)
logger.info(f"动态函数测试成功: {function_name} (脚本: {function_info['script_id']}, 耗时: {execution_time_ms}ms)")
return success_response(
data={
"result": result,
"execution_time_ms": execution_time_ms,
"function_name": function_name,
"script_id": function_info["script_id"]
},
message=f"函数测试成功"
)
except Exception as e:
logger.error(f"测试动态函数失败: {function_name} - {e}", exc_info=True)
# 更新失败统计
if function_info:
execution_time_ms = int((time.time() - start_time) * 1000) if 'start_time' in locals() else 0
registry.update_function_call_stats(function_name, execution_time_ms, False)
return error_response(f"函数测试失败: {str(e)}")