|
|
|
|
|
"""管理后台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))
|