You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
8.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""管理后台API路由 - 对应Go的api/admin_router.go"""
from fastapi import APIRouter, Depends, HTTPException, Header, Query
from typing import Optional
from app.models import (
Response, ConfigListRequest, ConfigUpdateRequest,
ReloadRequest, AdapterToggleRequest, AdapterConfigUpdateRequest,
APITestRequest, WSTestRequest, TestHistoryRequest
)
from app.services import ConfigService, AdapterService, TestService
from app.core.config import get_config
admin_router = APIRouter()
# 服务实例
config_service = ConfigService()
adapter_service = AdapterService()
test_service = TestService()
def verify_admin_token(x_admin_token: Optional[str] = Header(None)):
"""验证Admin Token"""
# TODO: 实现Token验证
return x_admin_token
# ============================================
# 系统管理接口
# ============================================
@admin_router.get("/admin/system/status", response_model=Response)
def get_system_status(
token: str = Depends(verify_admin_token)
):
"""获取系统状态"""
try:
data = config_service.get_system_status()
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/system/reload", response_model=Response)
def reload_config(
req: Optional[ReloadRequest] = None,
token: str = Depends(verify_admin_token)
):
"""热加载配置"""
try:
if req is None:
req = ReloadRequest()
data = config_service.reload_config(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/system/restart", response_model=Response)
def restart_service(
token: str = Depends(verify_admin_token)
):
"""重启服务
注意: 此方法通过创建子进程实现服务重启,适用于开发环境。
生产环境建议使用Docker或systemd管理服务生命周期。
"""
import os
import sys
import subprocess
import threading
import time
def delayed_restart():
"""延迟重启函数"""
time.sleep(2) # 等待当前响应返回
# 获取当前Python解释器和启动参数
python = sys.executable
args = sys.argv[:]
# 在Windows上使用start命令在Linux上使用nohup
if os.name == 'nt': # Windows
subprocess.Popen(
['start', 'python'] + args,
shell=True,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
else: # Linux/Mac
subprocess.Popen(
[python] + args,
stdout=open('/dev/null', 'w'),
stderr=open('/dev/null', 'w'),
start_new_session=True
)
# 退出当前进程
os._exit(0)
# 在后台线程中执行重启
restart_thread = threading.Thread(target=delayed_restart, daemon=True)
restart_thread.start()
return Response(
code=0,
message="服务将在2秒后重启",
data={"status": "restarting", "delay_seconds": 2}
)
# ============================================
# 配置管理接口
# ============================================
@admin_router.get("/admin/config", response_model=Response)
def get_config_list(
type: Optional[str] = Query(None, description="配置类型筛选"),
token: str = Depends(verify_admin_token)
):
"""获取配置列表"""
try:
from app.models import ConfigType
req = ConfigListRequest()
if type:
req.type = ConfigType(type)
data = config_service.get_config_list(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.put("/admin/config", response_model=Response)
def update_config(
req: ConfigUpdateRequest,
token: str = Depends(verify_admin_token)
):
"""更新配置"""
try:
data = config_service.update_config(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/config/reload", response_model=Response)
def reload_config_endpoint(
req: Optional[ReloadRequest] = None,
token: str = Depends(verify_admin_token)
):
"""热加载配置"""
return reload_config(req, token)
# ============================================
# 适配器管理接口
# ============================================
@admin_router.get("/admin/adapters", response_model=Response)
def get_adapter_list(
token: str = Depends(verify_admin_token)
):
"""获取适配器列表"""
try:
data = adapter_service.get_adapter_list()
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/adapters/toggle", response_model=Response)
def toggle_adapter(
req: AdapterToggleRequest,
token: str = Depends(verify_admin_token)
):
"""切换适配器状态"""
try:
adapter_service.toggle_adapter(req)
return Response(code=0, message="success")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.put("/admin/adapters/config", response_model=Response)
def update_adapter_config(
req: AdapterConfigUpdateRequest,
token: str = Depends(verify_admin_token)
):
"""更新适配器配置"""
try:
adapter_service.update_adapter_config(req)
return Response(code=0, message="success")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# ============================================
# 测试管理接口
# ============================================
@admin_router.get("/admin/tests/api", response_model=Response)
def get_api_test_list(
token: str = Depends(verify_admin_token)
):
"""获取API测试列表"""
try:
data = test_service.get_api_test_list()
# 设置基础URL
config = get_config()
data.base_url = f"http://localhost:{config.server.port}"
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/tests/api/run", response_model=Response)
async def run_api_test(
req: APITestRequest,
token: str = Depends(verify_admin_token)
):
"""执行API测试"""
try:
config = get_config()
base_url = f"http://localhost:{config.server.port}"
data = await test_service.run_api_test(base_url, req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.get("/admin/tests/ws", response_model=Response)
def get_ws_test_list(
token: str = Depends(verify_admin_token)
):
"""获取WebSocket测试列表"""
try:
data = test_service.get_ws_test_list()
config = get_config()
data.ws_url = f"ws://localhost:{config.server.port}/v1/stream"
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.post("/admin/tests/ws/run", response_model=Response)
async def run_ws_test(
req: WSTestRequest,
token: str = Depends(verify_admin_token)
):
"""执行WebSocket测试"""
try:
config = get_config()
ws_url = f"ws://localhost:{config.server.port}/v1/stream"
data = await test_service.run_ws_test(ws_url, req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@admin_router.get("/admin/tests/history", response_model=Response)
def get_test_history(
type: Optional[str] = Query(None, description="测试类型"),
limit: int = Query(default=20, ge=1, le=100, description="数量限制"),
token: str = Depends(verify_admin_token)
):
"""获取测试历史"""
try:
req = TestHistoryRequest(type=type, limit=limit)
data = test_service.get_test_history(req)
return Response(code=0, message="success", data=data)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))