VWED_server/routes/script_api.py

358 lines
12 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路由
提供脚本项目和文件的CRUD操作接口
"""
from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
from fastapi.responses import JSONResponse
from typing import List, Optional, Dict, Any
from pydantic import BaseModel
from services.script_file_service import get_file_service
from services.script_engine_service import get_script_engine
from utils.api_response import success_response, error_response
from utils.logger import get_logger
logger = get_logger("routes.script_api")
router = APIRouter(prefix="/api/script", tags=["在线脚本模块"])
# 请求模型定义
class ProjectCreateRequest(BaseModel):
project_name: str
description: Optional[str] = ""
created_by: Optional[str] = None
class FileCreateRequest(BaseModel):
project_id: int
file_name: str
file_path: str
content: Optional[str] = ""
file_type: Optional[str] = "python"
created_by: Optional[str] = None
class FileUpdateRequest(BaseModel):
content: str
updated_by: Optional[str] = None
class ScriptStartRequest(BaseModel):
script_path: str
start_params: Optional[Dict[str, Any]] = None
class FunctionCallRequest(BaseModel):
script_id: str
function_name: str
function_args: Any = None
@router.post("/projects", summary="创建脚本项目")
async def create_project(request: ProjectCreateRequest):
"""创建新的脚本项目"""
try:
file_service = get_file_service()
result = await file_service.create_project(
project_name=request.project_name,
description=request.description,
created_by=request.created_by
)
if result["success"]:
return result
# return success_response(data=result["project"], message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"创建项目失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"创建项目失败: {str(e)}")
@router.get("/projects", summary="获取项目列表")
async def get_projects(status: Optional[str] = "active"):
"""获取所有脚本项目"""
try:
file_service = get_file_service()
result = await file_service.get_projects(status=status)
if result["success"]:
return success_response(data=result["projects"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"获取项目列表失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取项目列表失败: {str(e)}")
@router.get("/projects/{project_id}/files", summary="获取项目文件列表")
async def get_project_files(project_id: int, include_content: bool = False):
"""获取指定项目的所有文件"""
try:
file_service = get_file_service()
result = await file_service.get_project_files(
project_id=project_id,
include_content=include_content
)
if result["success"]:
return success_response(data=result)
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"获取项目文件列表失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取项目文件列表失败: {str(e)}")
@router.post("/files", summary="创建脚本文件")
async def create_file(request: FileCreateRequest):
"""创建新的脚本文件"""
try:
file_service = get_file_service()
result = await file_service.create_file(
project_id=request.project_id,
file_name=request.file_name,
file_path=request.file_path,
content=request.content,
file_type=request.file_type,
created_by=request.created_by
)
if result["success"]:
return success_response(data=result["file"], message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"创建文件失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"创建文件失败: {str(e)}")
@router.get("/files/{file_id}", summary="获取文件内容")
async def get_file_content(file_id: int):
"""获取脚本文件的内容"""
try:
file_service = get_file_service()
result = await file_service.get_file_content(file_id=file_id)
if result["success"]:
return success_response(data=result)
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"获取文件内容失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取文件内容失败: {str(e)}")
@router.put("/files/{file_id}", summary="更新文件内容")
async def update_file_content(file_id: int, request: FileUpdateRequest):
"""更新脚本文件的内容"""
try:
file_service = get_file_service()
result = await file_service.update_file_content(
file_id=file_id,
content=request.content,
updated_by=request.updated_by
)
if result["success"]:
return success_response(data=result["file"], message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"更新文件内容失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"更新文件内容失败: {str(e)}")
@router.delete("/files/{file_id}", summary="删除文件")
async def delete_file(file_id: int):
"""删除脚本文件"""
try:
file_service = get_file_service()
result = await file_service.delete_file(file_id=file_id)
if result["success"]:
return success_response(message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"删除文件失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"删除文件失败: {str(e)}")
@router.get("/files/search", summary="搜索文件")
async def search_files(
keyword: str,
project_id: Optional[int] = None,
file_type: Optional[str] = None,
has_boot: Optional[bool] = None
):
"""搜索脚本文件"""
try:
file_service = get_file_service()
result = await file_service.search_files(
keyword=keyword,
project_id=project_id,
file_type=file_type,
has_boot=has_boot
)
if result["success"]:
return success_response(data=result)
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"搜索文件失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"搜索文件失败: {str(e)}")
@router.post("/validate-syntax", summary="验证脚本语法")
async def validate_syntax(request: dict):
"""验证Python脚本语法"""
try:
content = request.get("content", "")
if not content:
return error_response(message="脚本内容不能为空")
file_service = get_file_service()
result = await file_service.validate_script_syntax(content)
if result["success"]:
return success_response(data=result)
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"语法验证失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"语法验证失败: {str(e)}")
@router.post("/scripts/start", summary="启动脚本服务")
async def start_script_service(request: ScriptStartRequest):
"""启动脚本服务"""
try:
script_engine = get_script_engine()
result = await script_engine.start_script_service(
script_path=request.script_path,
start_params=request.start_params
)
if result["success"]:
return success_response(data=result, message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"启动脚本服务失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"启动脚本服务失败: {str(e)}")
@router.post("/scripts/{script_id}/stop", summary="停止脚本服务")
async def stop_script_service(script_id: str):
"""停止脚本服务"""
try:
script_engine = get_script_engine()
result = await script_engine.stop_script_service(script_id=script_id)
if result["success"]:
return success_response(message=result["message"])
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"停止脚本服务失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"停止脚本服务失败: {str(e)}")
@router.get("/scripts/running", summary="获取运行中的脚本")
async def get_running_scripts():
"""获取所有运行中的脚本"""
try:
script_engine = get_script_engine()
running_scripts = script_engine.get_running_scripts()
return success_response(data=running_scripts)
except Exception as e:
logger.error(f"获取运行中的脚本失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取运行中的脚本失败: {str(e)}")
@router.post("/scripts/execute-function", summary="执行脚本函数")
async def execute_function(request: FunctionCallRequest):
"""执行脚本中的指定函数"""
try:
script_engine = get_script_engine()
result = await script_engine.execute_script_function(
script_id=request.script_id,
function_name=request.function_name,
function_args=request.function_args
)
if result["success"]:
return success_response(data=result)
else:
return error_response(message=result["error"])
except Exception as e:
logger.error(f"执行脚本函数失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"执行脚本函数失败: {str(e)}")
@router.get("/registry/status", summary="获取注册中心状态")
async def get_registry_status():
"""获取脚本注册中心状态"""
try:
from services.script_registry_service import get_global_registry
registry = get_global_registry()
registrations = registry.get_all_registrations()
return success_response(data=registrations)
except Exception as e:
logger.error(f"获取注册中心状态失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取注册中心状态失败: {str(e)}")
@router.post("/upload", summary="上传脚本文件")
async def upload_script_file(
project_id: int,
file: UploadFile = File(...)
):
"""上传脚本文件"""
try:
# 读取文件内容
content = await file.read()
content_str = content.decode('utf-8')
file_service = get_file_service()
result = await file_service.create_file(
project_id=project_id,
file_name=file.filename,
file_path=file.filename,
content=content_str,
file_type="python"
)
if result["success"]:
return success_response(data=result["file"], message=result["message"])
else:
return error_response(message=result["error"])
except UnicodeDecodeError:
return error_response(message="文件编码错误请确保文件为UTF-8编码")
except Exception as e:
logger.error(f"上传脚本文件失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"上传脚本文件失败: {str(e)}")