1233 lines
40 KiB
Python
1233 lines
40 KiB
Python
"""算法服务管理路由,提供服务注册、管理等功能"""
|
||
|
||
from fastapi import APIRouter, HTTPException, status, Depends
|
||
from typing import List, Dict, Any, Optional
|
||
from pydantic import BaseModel
|
||
import uuid
|
||
import os
|
||
|
||
from app.config.settings import settings
|
||
from app.models.models import AlgorithmService, ServiceGroup, AlgorithmRepository
|
||
from app.models.database import SessionLocal
|
||
from app.routes.user import get_current_active_user
|
||
from app.schemas.user import UserResponse
|
||
from app.services.project_analyzer import ProjectAnalyzer
|
||
from app.services.service_generator import ServiceGenerator
|
||
from app.services.service_orchestrator import ServiceOrchestrator
|
||
from app.gitea.service import gitea_service
|
||
|
||
router = APIRouter(prefix="/services", tags=["services"])
|
||
|
||
|
||
class RegisterServiceRequest(BaseModel):
|
||
"""注册服务请求"""
|
||
repository_id: str
|
||
name: str
|
||
version: str = "1.0.0"
|
||
description: Optional[str] = ""
|
||
tech_category: str = "computer_vision"
|
||
output_type: str = "image"
|
||
service_type: str = "http"
|
||
host: str = "0.0.0.0"
|
||
port: int = 8000
|
||
timeout: int = 30
|
||
health_check_path: str = "/health"
|
||
environment: Dict[str, str] = {}
|
||
|
||
|
||
class ServiceResponse(BaseModel):
|
||
"""服务响应"""
|
||
id: str
|
||
service_id: str
|
||
name: str
|
||
algorithm_name: str
|
||
version: str
|
||
host: str
|
||
port: int
|
||
api_url: str
|
||
status: str
|
||
created_at: str
|
||
updated_at: Optional[str]
|
||
|
||
|
||
class ServiceListResponse(BaseModel):
|
||
"""服务列表响应"""
|
||
success: bool
|
||
services: List[ServiceResponse]
|
||
|
||
|
||
class ServiceDetailResponse(BaseModel):
|
||
"""服务详情响应"""
|
||
success: bool
|
||
service: ServiceResponse
|
||
|
||
|
||
class ServiceOperationResponse(BaseModel):
|
||
"""服务操作响应"""
|
||
success: bool
|
||
message: str
|
||
service_id: str
|
||
status: str
|
||
|
||
|
||
class ServiceStatusResponse(BaseModel):
|
||
"""服务状态响应"""
|
||
success: bool
|
||
status: str
|
||
health: str
|
||
|
||
|
||
class ServiceLogsResponse(BaseModel):
|
||
"""服务日志响应"""
|
||
success: bool
|
||
logs: List[str]
|
||
|
||
|
||
class RepositoryAlgorithmsResponse(BaseModel):
|
||
"""仓库算法列表响应"""
|
||
success: bool
|
||
algorithms: List[Dict[str, Any]]
|
||
|
||
|
||
class ServiceGroupRequest(BaseModel):
|
||
"""服务分组请求"""
|
||
name: str
|
||
description: str = ""
|
||
|
||
|
||
class ServiceGroupResponse(BaseModel):
|
||
"""服务分组响应"""
|
||
id: str
|
||
name: str
|
||
description: str
|
||
status: str
|
||
created_at: str
|
||
updated_at: str
|
||
|
||
|
||
class ServiceGroupListResponse(BaseModel):
|
||
"""服务分组列表响应"""
|
||
success: bool
|
||
groups: List[ServiceGroupResponse]
|
||
|
||
|
||
class ServiceGroupDetailResponse(BaseModel):
|
||
"""服务分组详情响应"""
|
||
success: bool
|
||
group: ServiceGroupResponse
|
||
|
||
|
||
class BatchOperationRequest(BaseModel):
|
||
"""批量操作请求"""
|
||
service_ids: List[str]
|
||
|
||
|
||
class BatchOperationResponse(BaseModel):
|
||
"""批量操作响应"""
|
||
success: bool
|
||
message: str
|
||
results: List[Dict[str, Any]]
|
||
|
||
|
||
# 初始化服务组件
|
||
project_analyzer = ProjectAnalyzer()
|
||
service_generator = ServiceGenerator()
|
||
service_orchestrator = ServiceOrchestrator(deployment_mode=settings.DEPLOYMENT_MODE)
|
||
|
||
|
||
@router.post("/register", status_code=status.HTTP_201_CREATED)
|
||
async def register_service(
|
||
request: RegisterServiceRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""注册新服务"""
|
||
# 检查用户权限
|
||
print(f"用户角色: {current_user.role_name}")
|
||
print(f"用户角色对象: {current_user.role}")
|
||
if not hasattr(current_user, 'role_name') or current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 1. 获取仓库信息
|
||
repo = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == request.repository_id).first()
|
||
if not repo:
|
||
raise HTTPException(status_code=404, detail="仓库不存在")
|
||
|
||
# 记录仓库信息
|
||
print(f"仓库信息: {repo.name}, {repo.description}, {repo.repo_url}")
|
||
|
||
# 2. 从Gitea仓库克隆代码到本地
|
||
repo_path = f"/tmp/algorithms/{request.repository_id}"
|
||
|
||
# 使用Gitea服务克隆仓库
|
||
clone_success = gitea_service.clone_repository(repo.repo_url, request.repository_id, repo.branch or "main")
|
||
if not clone_success:
|
||
raise HTTPException(status_code=400, detail=f"克隆仓库失败: {repo.repo_url}")
|
||
|
||
print(f"仓库克隆成功: {repo_path}")
|
||
|
||
# 3. 分析项目
|
||
project_info = project_analyzer.analyze_project(repo_path)
|
||
if not project_info["success"]:
|
||
raise HTTPException(status_code=400, detail=f"项目分析失败: {project_info['error']}")
|
||
|
||
print(f"项目分析成功: {project_info}")
|
||
|
||
# 4. 生成服务包装器
|
||
service_config = {
|
||
"name": request.name,
|
||
"version": request.version,
|
||
"service_type": request.service_type,
|
||
"host": request.host,
|
||
"port": request.port,
|
||
"timeout": request.timeout,
|
||
"health_check_path": request.health_check_path,
|
||
"environment": request.environment
|
||
}
|
||
|
||
generate_result = service_generator.generate_service(project_info, service_config)
|
||
if not generate_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"服务生成失败: {generate_result['error']}")
|
||
|
||
print(f"服务生成成功: {generate_result}")
|
||
|
||
# 5. 部署服务
|
||
service_id = str(uuid.uuid4())
|
||
deploy_result = service_orchestrator.deploy_service(service_id, service_config, project_info, repo_path)
|
||
if not deploy_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"服务部署失败: {deploy_result['error']}")
|
||
|
||
print(f"服务部署成功: {deploy_result}")
|
||
|
||
# 6. 保存服务信息到数据库
|
||
new_service = AlgorithmService(
|
||
id=str(uuid.uuid4()),
|
||
service_id=service_id,
|
||
name=request.name,
|
||
algorithm_name=repo.name, # 使用仓库名称作为算法名称
|
||
version=request.version,
|
||
tech_category=request.tech_category,
|
||
output_type=request.output_type,
|
||
host=request.host,
|
||
port=request.port,
|
||
api_url=deploy_result["api_url"],
|
||
status=deploy_result["status"],
|
||
config={
|
||
"repository_id": request.repository_id, # 保存仓库ID
|
||
"service_type": request.service_type,
|
||
"timeout": request.timeout,
|
||
"health_check_path": request.health_check_path,
|
||
"environment": request.environment,
|
||
"container_id": deploy_result["container_id"]
|
||
}
|
||
)
|
||
|
||
db.add(new_service)
|
||
db.commit()
|
||
db.refresh(new_service)
|
||
|
||
# 6. 返回响应
|
||
return {
|
||
"success": True,
|
||
"message": "服务注册成功",
|
||
"service": {
|
||
"id": new_service.id,
|
||
"service_id": new_service.service_id,
|
||
"name": new_service.name,
|
||
"algorithm_name": new_service.algorithm_name,
|
||
"version": new_service.version,
|
||
"host": new_service.host,
|
||
"port": new_service.port,
|
||
"api_url": new_service.api_url,
|
||
"status": new_service.status,
|
||
"created_at": new_service.created_at.isoformat(),
|
||
"updated_at": new_service.updated_at.isoformat() if new_service.updated_at else None
|
||
}
|
||
}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("", response_model=ServiceListResponse)
|
||
async def list_services(
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务列表"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务列表
|
||
services = db.query(AlgorithmService).all()
|
||
|
||
# 转换为响应格式
|
||
service_list = []
|
||
for service in services:
|
||
service_list.append(ServiceResponse(
|
||
id=service.id,
|
||
service_id=service.service_id,
|
||
name=service.name,
|
||
algorithm_name=service.algorithm_name,
|
||
version=service.version,
|
||
host=service.host,
|
||
port=service.port,
|
||
api_url=service.api_url,
|
||
status=service.status,
|
||
created_at=service.created_at.isoformat(),
|
||
updated_at=service.updated_at.isoformat() if service.updated_at else None
|
||
))
|
||
|
||
return ServiceListResponse(
|
||
success=True,
|
||
services=service_list
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/{service_id}", response_model=ServiceDetailResponse)
|
||
async def get_service(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务详情"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 返回响应
|
||
return ServiceDetailResponse(
|
||
success=True,
|
||
service=ServiceResponse(
|
||
id=service.id,
|
||
service_id=service.service_id,
|
||
name=service.name,
|
||
algorithm_name=service.algorithm_name,
|
||
version=service.version,
|
||
host=service.host,
|
||
port=service.port,
|
||
api_url=service.api_url,
|
||
status=service.status,
|
||
created_at=service.created_at.isoformat(),
|
||
updated_at=service.updated_at.isoformat() if service.updated_at else None
|
||
)
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/{service_id}/start")
|
||
async def start_service(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""启动服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
raise HTTPException(status_code=400, detail="Container ID not found")
|
||
|
||
# 启动服务
|
||
start_result = service_orchestrator.start_service(service_id, container_id)
|
||
|
||
# 如果启动失败,尝试从数据库重新注册服务
|
||
if not start_result["success"]:
|
||
print(f"服务启动失败: {start_result['error']},尝试从数据库重新注册服务")
|
||
|
||
# 获取仓库信息
|
||
repository_id = service.config.get("repository_id")
|
||
if not repository_id:
|
||
raise HTTPException(status_code=400, detail="Repository ID not found in service config")
|
||
|
||
repository = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == repository_id).first()
|
||
if not repository:
|
||
raise HTTPException(status_code=404, detail="Repository not found")
|
||
|
||
# 从Gitea克隆仓库
|
||
clone_success = gitea_service.clone_repository(
|
||
repository.repo_url,
|
||
service_id,
|
||
repository.branch or "main"
|
||
)
|
||
if not clone_success:
|
||
raise HTTPException(status_code=400, detail="Failed to clone repository")
|
||
|
||
# 仓库路径
|
||
repo_path = f"/tmp/algorithms/{service_id}"
|
||
|
||
# 分析项目
|
||
project_info = project_analyzer.analyze_project(repo_path)
|
||
if not project_info:
|
||
raise HTTPException(status_code=400, detail="Failed to analyze project")
|
||
|
||
# 生成服务
|
||
service_config = {
|
||
"name": service.name,
|
||
"version": service.version,
|
||
"host": service.host,
|
||
"port": service.port,
|
||
"timeout": service.config.get("timeout", 30),
|
||
"health_check_path": service.config.get("health_check_path", "/health"),
|
||
"environment": service.config.get("environment", {})
|
||
}
|
||
|
||
# 部署服务
|
||
deploy_result = service_orchestrator.deploy_service(service_id, project_info, service_config, repo_path)
|
||
if not deploy_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"服务部署失败: {deploy_result['error']}")
|
||
|
||
# 更新服务配置
|
||
service.config["container_id"] = deploy_result["container_id"]
|
||
service.api_url = deploy_result["api_url"]
|
||
db.commit()
|
||
|
||
start_result = {
|
||
"success": True,
|
||
"service_id": service_id,
|
||
"status": "running",
|
||
"error": None
|
||
}
|
||
|
||
# 更新服务状态
|
||
service.status = start_result["status"]
|
||
db.commit()
|
||
|
||
# 返回响应
|
||
return ServiceOperationResponse(
|
||
success=True,
|
||
message="服务启动成功",
|
||
service_id=service_id,
|
||
status=start_result["status"]
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/{service_id}/stop")
|
||
async def stop_service(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""停止服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
raise HTTPException(status_code=400, detail="Container ID not found")
|
||
|
||
# 停止服务
|
||
stop_result = service_orchestrator.stop_service(service_id, container_id)
|
||
if not stop_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"服务停止失败: {stop_result['error']}")
|
||
|
||
# 更新服务状态
|
||
service.status = stop_result["status"]
|
||
db.commit()
|
||
|
||
# 返回响应
|
||
return ServiceOperationResponse(
|
||
success=True,
|
||
message="服务停止成功",
|
||
service_id=service_id,
|
||
status=stop_result["status"]
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/{service_id}/restart")
|
||
async def restart_service(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""重启服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
raise HTTPException(status_code=400, detail="Container ID not found")
|
||
|
||
# 重启服务
|
||
restart_result = service_orchestrator.restart_service(service_id, container_id)
|
||
if not restart_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"服务重启失败: {restart_result['error']}")
|
||
|
||
# 更新服务状态
|
||
service.status = restart_result["status"]
|
||
db.commit()
|
||
|
||
# 返回响应
|
||
return ServiceOperationResponse(
|
||
success=True,
|
||
message="服务重启成功",
|
||
service_id=service_id,
|
||
status=restart_result["status"]
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.delete("/{service_id}")
|
||
async def delete_service(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""删除服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID和镜像名称
|
||
container_id = service.config.get("container_id")
|
||
image_name = f"algorithm-service-{service_id}:{service.version}"
|
||
|
||
# 删除服务
|
||
delete_result = service_orchestrator.delete_service(service_id, container_id, image_name)
|
||
if not delete_result["success"]:
|
||
# 继续执行,即使Docker操作失败
|
||
pass
|
||
|
||
# 删除数据库记录
|
||
db.delete(service)
|
||
db.commit()
|
||
|
||
# 返回响应
|
||
return {
|
||
"success": True,
|
||
"message": "服务删除成功",
|
||
"service_id": service_id
|
||
}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/{service_id}/status")
|
||
async def get_service_status(
|
||
service_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务状态"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
raise HTTPException(status_code=400, detail="Container ID not found")
|
||
|
||
# 获取服务状态
|
||
status_result = service_orchestrator.get_service_status(container_id)
|
||
if not status_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"获取服务状态失败: {status_result['error']}")
|
||
|
||
# 返回响应
|
||
return ServiceStatusResponse(
|
||
success=True,
|
||
status=status_result["status"],
|
||
health=status_result["health"]
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/{service_id}/logs")
|
||
async def get_service_logs(
|
||
service_id: str,
|
||
lines: int = 100,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务日志"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
raise HTTPException(status_code=400, detail="Container ID not found")
|
||
|
||
# 获取服务日志
|
||
logs_result = service_orchestrator.get_service_logs(container_id, lines)
|
||
if not logs_result["success"]:
|
||
raise HTTPException(status_code=400, detail=f"获取服务日志失败: {logs_result['error']}")
|
||
|
||
# 返回响应
|
||
return ServiceLogsResponse(
|
||
success=True,
|
||
logs=logs_result["logs"]
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/repository/algorithms")
|
||
async def get_repository_algorithms(
|
||
repository_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取仓库中的算法列表"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
try:
|
||
# 模拟获取仓库中的算法列表
|
||
# 注意:在实际实现中,应该从算法仓库中获取真实的算法列表
|
||
algorithms = [
|
||
{
|
||
"id": "alg-001",
|
||
"name": "图像分类算法",
|
||
"description": "基于深度学习的图像分类算法",
|
||
"type": "computer_vision",
|
||
"entry_point": "algorithm.py"
|
||
},
|
||
{
|
||
"id": "alg-002",
|
||
"name": "文本分类算法",
|
||
"description": "基于BERT的文本分类算法",
|
||
"type": "nlp",
|
||
"entry_point": "text_algorithm.py"
|
||
}
|
||
]
|
||
|
||
return RepositoryAlgorithmsResponse(
|
||
success=True,
|
||
algorithms=algorithms
|
||
)
|
||
except Exception as e:
|
||
raise HTTPException(status_code=500, detail=str(e))
|
||
|
||
|
||
# 服务分组管理API
|
||
|
||
@router.post("/groups", status_code=status.HTTP_201_CREATED)
|
||
async def create_service_group(
|
||
request: ServiceGroupRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""创建服务分组"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 生成唯一ID
|
||
group_id = str(uuid.uuid4())
|
||
|
||
# 创建分组实例
|
||
group = ServiceGroup(
|
||
id=group_id,
|
||
name=request.name,
|
||
description=request.description
|
||
)
|
||
|
||
# 保存到数据库
|
||
db.add(group)
|
||
db.commit()
|
||
db.refresh(group)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "服务分组创建成功",
|
||
"group": {
|
||
"id": group.id,
|
||
"name": group.name,
|
||
"description": group.description,
|
||
"status": group.status,
|
||
"created_at": group.created_at.isoformat(),
|
||
"updated_at": group.updated_at.isoformat()
|
||
}
|
||
}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/groups", response_model=ServiceGroupListResponse)
|
||
async def list_service_groups(
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务分组列表"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询分组列表
|
||
groups = db.query(ServiceGroup).all()
|
||
|
||
# 转换为响应格式
|
||
group_list = []
|
||
for group in groups:
|
||
group_list.append(ServiceGroupResponse(
|
||
id=group.id,
|
||
name=group.name,
|
||
description=group.description,
|
||
status=group.status,
|
||
created_at=group.created_at.isoformat(),
|
||
updated_at=group.updated_at.isoformat()
|
||
))
|
||
|
||
return ServiceGroupListResponse(
|
||
success=True,
|
||
groups=group_list
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.get("/groups/{group_id}", response_model=ServiceGroupDetailResponse)
|
||
async def get_service_group(
|
||
group_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""获取服务分组详情"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询分组
|
||
group = db.query(ServiceGroup).filter(ServiceGroup.id == group_id).first()
|
||
|
||
if not group:
|
||
raise HTTPException(status_code=404, detail="Service group not found")
|
||
|
||
return ServiceGroupDetailResponse(
|
||
success=True,
|
||
group=ServiceGroupResponse(
|
||
id=group.id,
|
||
name=group.name,
|
||
description=group.description,
|
||
status=group.status,
|
||
created_at=group.created_at.isoformat(),
|
||
updated_at=group.updated_at.isoformat()
|
||
)
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.put("/groups/{group_id}")
|
||
async def update_service_group(
|
||
group_id: str,
|
||
request: ServiceGroupRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""更新服务分组"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询分组
|
||
group = db.query(ServiceGroup).filter(ServiceGroup.id == group_id).first()
|
||
|
||
if not group:
|
||
raise HTTPException(status_code=404, detail="Service group not found")
|
||
|
||
# 更新分组信息
|
||
group.name = request.name
|
||
group.description = request.description
|
||
|
||
# 保存到数据库
|
||
db.commit()
|
||
db.refresh(group)
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "服务分组更新成功",
|
||
"group": {
|
||
"id": group.id,
|
||
"name": group.name,
|
||
"description": group.description,
|
||
"status": group.status,
|
||
"created_at": group.created_at.isoformat(),
|
||
"updated_at": group.updated_at.isoformat()
|
||
}
|
||
}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.delete("/groups/{group_id}")
|
||
async def delete_service_group(
|
||
group_id: str,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""删除服务分组"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询分组
|
||
group = db.query(ServiceGroup).filter(ServiceGroup.id == group_id).first()
|
||
|
||
if not group:
|
||
raise HTTPException(status_code=404, detail="Service group not found")
|
||
|
||
# 检查分组是否有服务
|
||
services_count = db.query(AlgorithmService).filter(AlgorithmService.group_id == group_id).count()
|
||
if services_count > 0:
|
||
raise HTTPException(status_code=400, detail=f"该分组下还有{services_count}个服务,无法删除")
|
||
|
||
# 删除分组
|
||
db.delete(group)
|
||
db.commit()
|
||
|
||
return {
|
||
"success": True,
|
||
"message": "服务分组删除成功",
|
||
"group_id": group_id
|
||
}
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
# 批量服务操作API
|
||
|
||
@router.post("/batch/start")
|
||
async def batch_start_services(
|
||
request: BatchOperationRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""批量启动服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
results = []
|
||
success_count = 0
|
||
|
||
for service_id in request.service_ids:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "服务不存在"
|
||
})
|
||
continue
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "容器ID不存在"
|
||
})
|
||
continue
|
||
|
||
# 启动服务
|
||
start_result = service_orchestrator.start_service(service_id, container_id)
|
||
if start_result["success"]:
|
||
# 更新服务状态
|
||
service.status = start_result["status"]
|
||
db.commit()
|
||
success_count += 1
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": True,
|
||
"message": "服务启动成功"
|
||
})
|
||
else:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": f"服务启动失败: {start_result['error']}"
|
||
})
|
||
|
||
return BatchOperationResponse(
|
||
success=True,
|
||
message=f"批量启动完成,成功{success_count}个,失败{len(request.service_ids) - success_count}个",
|
||
results=results
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/batch/stop")
|
||
async def batch_stop_services(
|
||
request: BatchOperationRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""批量停止服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
results = []
|
||
success_count = 0
|
||
|
||
for service_id in request.service_ids:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "服务不存在"
|
||
})
|
||
continue
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "容器ID不存在"
|
||
})
|
||
continue
|
||
|
||
# 停止服务
|
||
stop_result = service_orchestrator.stop_service(service_id, container_id)
|
||
if stop_result["success"]:
|
||
# 更新服务状态
|
||
service.status = stop_result["status"]
|
||
db.commit()
|
||
success_count += 1
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": True,
|
||
"message": "服务停止成功"
|
||
})
|
||
else:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": f"服务停止失败: {stop_result['error']}"
|
||
})
|
||
|
||
return BatchOperationResponse(
|
||
success=True,
|
||
message=f"批量停止完成,成功{success_count}个,失败{len(request.service_ids) - success_count}个",
|
||
results=results
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/batch/restart")
|
||
async def batch_restart_services(
|
||
request: BatchOperationRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""批量重启服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
results = []
|
||
success_count = 0
|
||
|
||
for service_id in request.service_ids:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "服务不存在"
|
||
})
|
||
continue
|
||
|
||
# 获取容器ID
|
||
container_id = service.config.get("container_id")
|
||
if not container_id:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "容器ID不存在"
|
||
})
|
||
continue
|
||
|
||
# 重启服务
|
||
restart_result = service_orchestrator.restart_service(service_id, container_id)
|
||
if restart_result["success"]:
|
||
# 更新服务状态
|
||
service.status = restart_result["status"]
|
||
db.commit()
|
||
success_count += 1
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": True,
|
||
"message": "服务重启成功"
|
||
})
|
||
else:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": f"服务重启失败: {restart_result['error']}"
|
||
})
|
||
|
||
return BatchOperationResponse(
|
||
success=True,
|
||
message=f"批量重启完成,成功{success_count}个,失败{len(request.service_ids) - success_count}个",
|
||
results=results
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
@router.post("/batch/delete")
|
||
async def batch_delete_services(
|
||
request: BatchOperationRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""批量删除服务"""
|
||
# 检查用户权限
|
||
if current_user.role_name != "admin":
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
results = []
|
||
success_count = 0
|
||
|
||
for service_id in request.service_ids:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(AlgorithmService.service_id == service_id).first()
|
||
|
||
if not service:
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": False,
|
||
"message": "服务不存在"
|
||
})
|
||
continue
|
||
|
||
# 获取容器ID和镜像名称
|
||
container_id = service.config.get("container_id")
|
||
image_name = f"algorithm-service-{service_id}:{service.version}"
|
||
|
||
# 删除服务
|
||
delete_result = service_orchestrator.delete_service(service_id, container_id, image_name)
|
||
|
||
# 删除数据库记录
|
||
db.delete(service)
|
||
db.commit()
|
||
success_count += 1
|
||
results.append({
|
||
"service_id": service_id,
|
||
"success": True,
|
||
"message": "服务删除成功"
|
||
})
|
||
|
||
return BatchOperationResponse(
|
||
success=True,
|
||
message=f"批量删除完成,成功{success_count}个,失败{len(request.service_ids) - success_count}个",
|
||
results=results
|
||
)
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
class ServiceCallRequest(BaseModel):
|
||
"""服务调用请求"""
|
||
service_id: str
|
||
payload: Dict[str, Any]
|
||
|
||
|
||
class ServiceCallResponse(BaseModel):
|
||
"""服务调用响应"""
|
||
success: bool
|
||
result: Dict[str, Any]
|
||
service_id: str
|
||
execution_time: float
|
||
error: Optional[str] = None
|
||
|
||
|
||
@router.post("/call")
|
||
async def call_service(
|
||
request: ServiceCallRequest,
|
||
current_user: UserResponse = Depends(get_current_active_user)
|
||
):
|
||
"""直接调用注册的服务"""
|
||
import time
|
||
import httpx
|
||
|
||
# 创建数据库会话
|
||
db = SessionLocal()
|
||
try:
|
||
# 查询服务
|
||
service = db.query(AlgorithmService).filter(
|
||
AlgorithmService.service_id == request.service_id
|
||
).first()
|
||
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="服务不存在")
|
||
|
||
# 检查服务状态
|
||
if service.status != "running":
|
||
raise HTTPException(
|
||
status_code=503,
|
||
detail=f"服务未运行,当前状态: {service.status}"
|
||
)
|
||
|
||
# 调用服务
|
||
start_time = time.time()
|
||
|
||
try:
|
||
# 构建服务URL
|
||
service_url = service.api_url
|
||
|
||
# 如果URL没有路径,添加默认路径
|
||
if not service_url.endswith("/"):
|
||
service_url += "/"
|
||
|
||
# 添加调用端点
|
||
call_url = f"{service_url}predict"
|
||
|
||
# 使用httpx调用服务
|
||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||
response = await client.post(
|
||
call_url,
|
||
json=request.payload,
|
||
headers={"Content-Type": "application/json"}
|
||
)
|
||
|
||
execution_time = time.time() - start_time
|
||
|
||
if response.status_code == 200:
|
||
return ServiceCallResponse(
|
||
success=True,
|
||
result=response.json(),
|
||
service_id=request.service_id,
|
||
execution_time=execution_time
|
||
)
|
||
else:
|
||
return ServiceCallResponse(
|
||
success=False,
|
||
result={},
|
||
service_id=request.service_id,
|
||
execution_time=execution_time,
|
||
error=f"服务返回错误: HTTP {response.status_code} - {response.text}"
|
||
)
|
||
|
||
except httpx.RequestError as e:
|
||
execution_time = time.time() - start_time
|
||
return ServiceCallResponse(
|
||
success=False,
|
||
result={},
|
||
service_id=request.service_id,
|
||
execution_time=execution_time,
|
||
error=f"无法连接到服务: {str(e)}"
|
||
)
|
||
except Exception as e:
|
||
execution_time = time.time() - start_time
|
||
return ServiceCallResponse(
|
||
success=False,
|
||
result={},
|
||
service_id=request.service_id,
|
||
execution_time=execution_time,
|
||
error=f"服务调用异常: {str(e)}"
|
||
)
|
||
|
||
finally:
|
||
db.close()
|