first commit

This commit is contained in:
2026-02-08 14:42:58 +08:00
commit 20e1deae21
8197 changed files with 2264639 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
from fastapi import APIRouter
from app.routes import user, algorithm, api_key, history, gateway, monitoring, openai, deployment
api_router = APIRouter()
# 注册路由
api_router.include_router(user.router, prefix="/users", tags=["users"])
api_router.include_router(algorithm.router, prefix="/algorithms", tags=["algorithms"])
api_router.include_router(api_key.router, prefix="/api-keys", tags=["api-keys"])
api_router.include_router(history.router, prefix="/history", tags=["history"])
api_router.include_router(gateway.router, prefix="/gateway", tags=["gateway"])
api_router.include_router(monitoring.router, prefix="/monitoring", tags=["monitoring"])
api_router.include_router(openai.router, prefix="/openai", tags=["openai"])
api_router.include_router(deployment.router, tags=["deployment"])

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,392 @@
from fastapi import APIRouter, Depends, HTTPException, status, Body, UploadFile, File
from sqlalchemy.orm import Session
from typing import List, Optional
import os
import uuid
from app.utils.file import file_storage
from app.models.database import get_db
from app.schemas.algorithm import AlgorithmCreate, AlgorithmUpdate, AlgorithmResponse, AlgorithmListResponse, AlgorithmVersionCreate, AlgorithmVersionUpdate, AlgorithmVersionResponse, AlgorithmCallCreate, AlgorithmCallResult
from app.models.models import AlgorithmCall
from app.services.algorithm import AlgorithmService, AlgorithmVersionService, AlgorithmCallService
from app.dependencies import get_current_active_user
# 创建路由器
router = APIRouter(prefix="/algorithms", tags=["algorithms"])
@router.post("", response_model=AlgorithmResponse)
async def create_algorithm(
algorithm: AlgorithmCreate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""创建算法"""
# 只有管理员可以创建算法
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
# 创建算法
db_algorithm = AlgorithmService.create_algorithm(db, algorithm)
return db_algorithm
@router.get("", response_model=AlgorithmListResponse)
async def get_algorithms(
skip: int = 0,
limit: int = 100,
type: Optional[str] = None,
db: Session = Depends(get_db)
):
"""获取算法列表"""
algorithms = AlgorithmService.get_algorithms(db, skip=skip, limit=limit, algorithm_type=type)
return {"algorithms": algorithms, "total": len(algorithms)}
@router.get("/{algorithm_id}", response_model=AlgorithmResponse)
async def get_algorithm(
algorithm_id: str,
db: Session = Depends(get_db)
):
"""获取算法详情"""
algorithm = AlgorithmService.get_algorithm_by_id(db, algorithm_id)
if not algorithm:
raise HTTPException(status_code=404, detail="Algorithm not found")
return algorithm
@router.put("/{algorithm_id}", response_model=AlgorithmResponse)
async def update_algorithm(
algorithm_id: str,
algorithm_update: AlgorithmUpdate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""更新算法"""
# 只有管理员可以更新算法
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
algorithm = AlgorithmService.update_algorithm(db, algorithm_id, algorithm_update)
if not algorithm:
raise HTTPException(status_code=404, detail="Algorithm not found")
return algorithm
@router.delete("/{algorithm_id}", response_model=dict)
async def delete_algorithm(
algorithm_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""删除算法"""
# 只有管理员可以删除算法
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
success = AlgorithmService.delete_algorithm(db, algorithm_id)
if not success:
raise HTTPException(status_code=404, detail="Algorithm not found")
return {"message": "Algorithm deleted successfully"}
# 算法版本相关路由
@router.post("/{algorithm_id}/versions", response_model=AlgorithmVersionResponse)
async def create_version(
algorithm_id: str,
version: AlgorithmVersionCreate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""创建算法版本"""
# 只有管理员可以创建算法版本
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
# 验证算法是否存在
if not AlgorithmService.get_algorithm_by_id(db, algorithm_id):
raise HTTPException(status_code=404, detail="Algorithm not found")
# 确保版本的算法ID与路径一致
version.algorithm_id = algorithm_id
# 创建版本
db_version = AlgorithmVersionService.create_version(db, version)
return db_version
@router.get("/{algorithm_id}/versions", response_model=List[AlgorithmVersionResponse])
async def get_versions(
algorithm_id: str,
db: Session = Depends(get_db)
):
"""获取算法版本列表"""
# 验证算法是否存在
if not AlgorithmService.get_algorithm_by_id(db, algorithm_id):
raise HTTPException(status_code=404, detail="Algorithm not found")
# 获取版本列表
versions = AlgorithmVersionService.get_versions_by_algorithm_id(db, algorithm_id)
return versions
@router.get("/{algorithm_id}/versions/{version_id}", response_model=AlgorithmVersionResponse)
async def get_version(
algorithm_id: str,
version_id: str,
db: Session = Depends(get_db)
):
"""获取算法版本详情"""
# 验证算法是否存在
if not AlgorithmService.get_algorithm_by_id(db, algorithm_id):
raise HTTPException(status_code=404, detail="Algorithm not found")
# 获取版本
version = AlgorithmVersionService.get_version_by_id(db, version_id)
if not version:
raise HTTPException(status_code=404, detail="Version not found")
# 验证版本是否属于该算法
if version.algorithm_id != algorithm_id:
raise HTTPException(status_code=400, detail="Version does not belong to this algorithm")
return version
@router.put("/{algorithm_id}/versions/{version_id}", response_model=AlgorithmVersionResponse)
async def update_version(
algorithm_id: str,
version_id: str,
version_update: AlgorithmVersionUpdate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""更新算法版本"""
# 只有管理员可以更新算法版本
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
# 验证算法是否存在
if not AlgorithmService.get_algorithm_by_id(db, algorithm_id):
raise HTTPException(status_code=404, detail="Algorithm not found")
# 获取版本
version = AlgorithmVersionService.get_version_by_id(db, version_id)
if not version:
raise HTTPException(status_code=404, detail="Version not found")
# 验证版本是否属于该算法
if version.algorithm_id != algorithm_id:
raise HTTPException(status_code=400, detail="Version does not belong to this algorithm")
# 更新版本
updated_version = AlgorithmVersionService.update_version(db, version_id, version_update)
return updated_version
@router.delete("/{algorithm_id}/versions/{version_id}", response_model=dict)
async def delete_version(
algorithm_id: str,
version_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""删除算法版本"""
# 只有管理员可以删除算法版本
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
# 验证算法是否存在
if not AlgorithmService.get_algorithm_by_id(db, algorithm_id):
raise HTTPException(status_code=404, detail="Algorithm not found")
# 获取版本
version = AlgorithmVersionService.get_version_by_id(db, version_id)
if not version:
raise HTTPException(status_code=404, detail="Version not found")
# 验证版本是否属于该算法
if version.algorithm_id != algorithm_id:
raise HTTPException(status_code=400, detail="Version does not belong to this algorithm")
# 删除版本
success = AlgorithmVersionService.delete_version(db, version_id)
if not success:
raise HTTPException(status_code=400, detail="Failed to delete version")
return {"message": "Version deleted successfully"}
# 算法调用相关路由
@router.post("/call", response_model=AlgorithmCallResult)
async def call_algorithm(
call: AlgorithmCallCreate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""调用算法"""
# 执行算法
result = AlgorithmCallService.execute_algorithm(db, current_user.id, call)
return result
@router.get("/calls/{call_id}", response_model=AlgorithmCallResult)
async def get_call_result(
call_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取算法调用结果"""
# 获取调用记录
call = AlgorithmCallService.get_call_by_id(db, call_id)
if not call:
raise HTTPException(status_code=404, detail="Call not found")
# 管理员可以查看所有调用记录,普通用户只能查看自己的
if current_user.role != "admin" and current_user.id != call.user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
return call
@router.get("/calls", response_model=List[AlgorithmCallResult])
async def get_call_history(
skip: int = 0,
limit: int = 100,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取调用历史"""
# 管理员可以查看所有调用记录,普通用户只能查看自己的
if current_user.role == "admin":
# 这里可以添加分页和过滤,暂时返回所有
calls = db.query(AlgorithmCall).offset(skip).limit(limit).all()
else:
calls = AlgorithmCallService.get_calls_by_user_id(db, current_user.id, skip=skip, limit=limit)
return calls
# 代码执行相关路由
@router.post("/execute-code")
async def execute_code(
code: str = Body(..., description="Python代码"),
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""执行Python代码"""
# 执行代码
result = AlgorithmCallService.execute_python_code(code)
return result
# 模型文件上传路由
@router.post("/upload-model")
async def upload_model(
file: UploadFile = File(..., description="模型文件"),
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""上传模型文件"""
# 支持的文件类型
allowed_extensions = {
".pt", ".pth", ".h5", ".hdf5", ".onnx", ".pb", ".tflite",
".joblib", ".pkl", ".zip", ".tar.gz"
}
# 验证文件类型
file_extension = os.path.splitext(file.filename)[1].lower()
if file_extension not in allowed_extensions:
return {
"success": False,
"message": f"不支持的文件类型,支持的类型:{', '.join(allowed_extensions)}"
}
try:
# 生成唯一的文件名
unique_filename = f"models/{uuid.uuid4().hex[:8]}{file_extension}"
# 读取文件内容
file_content = await file.read()
# 上传文件到MinIO
import io
file_obj = io.BytesIO(file_content)
success = file_storage.upload_fileobj(file_obj, unique_filename, file.content_type)
if success:
return {
"success": True,
"file_path": unique_filename,
"message": "模型文件上传成功"
}
else:
return {
"success": False,
"message": "模型文件上传失败"
}
except Exception as e:
return {
"success": False,
"message": f"模型文件上传失败:{str(e)}"
}
# 视频文件上传路由
@router.post("/upload-video")
async def upload_video(
file: UploadFile = File(..., description="视频文件"),
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""上传视频文件"""
# 支持的视频文件类型
allowed_extensions = {
".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv", ".webm"
}
# 验证文件类型
file_extension = os.path.splitext(file.filename)[1].lower()
if file_extension not in allowed_extensions:
return {
"success": False,
"message": f"不支持的视频文件类型,支持的类型:{', '.join(allowed_extensions)}"
}
try:
# 生成唯一的文件名
unique_filename = f"videos/{current_user['id']}/{uuid.uuid4().hex[:12]}{file_extension}"
# 读取文件内容
file_content = await file.read()
# 上传文件到MinIO
import io
file_obj = io.BytesIO(file_content)
success = file_storage.upload_fileobj(file_obj, unique_filename, file.content_type)
if success:
return {
"success": True,
"file_path": unique_filename,
"message": "视频文件上传成功"
}
else:
return {
"success": False,
"message": "视频文件上传失败"
}
except Exception as e:
return {
"success": False,
"message": f"视频文件上传失败:{str(e)}"
}

View File

@@ -0,0 +1,88 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from app.models.database import get_db
from app.schemas.user import APIKeyCreate, APIKeyResponse, APIKeyListResponse
from app.models.models import APIKey
from app.services.user import APIKeyService
from app.dependencies import get_current_active_user
# 创建路由器
router = APIRouter(prefix="/api-keys", tags=["api-keys"])
@router.post("", response_model=APIKeyResponse)
async def create_api_key(
api_key_create: APIKeyCreate,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""创建API密钥"""
# 只有管理员或用户本人可以为自己创建API密钥
if current_user.role != "admin" and current_user.id != api_key_create.user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
# 创建API密钥
api_key = APIKeyService.create_api_key(db, api_key_create)
return api_key
@router.get("", response_model=APIKeyListResponse)
async def get_api_keys(
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取API密钥列表"""
# 管理员可以查看所有API密钥普通用户只能查看自己的
if current_user.role == "admin":
# 这里可以添加分页和过滤,暂时返回所有
api_keys = db.query(APIKey).all()
else:
api_keys = APIKeyService.get_api_keys_by_user_id(db, current_user.id)
return {"api_keys": api_keys, "total": len(api_keys)}
@router.get("/{api_key_id}", response_model=APIKeyResponse)
async def get_api_key(
api_key_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取API密钥详情"""
# 获取API密钥
api_key = APIKeyService.get_api_key_by_id(db, api_key_id)
if not api_key:
raise HTTPException(status_code=404, detail="API key not found")
# 管理员可以查看所有API密钥普通用户只能查看自己的
if current_user.role != "admin" and current_user.id != api_key.user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
return api_key
@router.delete("/{api_key_id}", response_model=dict)
async def revoke_api_key(
api_key_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""撤销API密钥"""
# 获取API密钥
api_key = APIKeyService.get_api_key_by_id(db, api_key_id)
if not api_key:
raise HTTPException(status_code=404, detail="API key not found")
# 管理员可以撤销所有API密钥普通用户只能撤销自己的
if current_user.role != "admin" and current_user.id != api_key.user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
# 撤销API密钥
result = APIKeyService.revoke_api_key(db, api_key_id)
if not result:
raise HTTPException(status_code=400, detail="Failed to revoke API key")
return {"message": "API key revoked successfully"}

View File

@@ -0,0 +1,345 @@
"""数据管理路由,提供输入数据、输出结果和元数据的管理功能"""
from fastapi import APIRouter, HTTPException, status, Depends, UploadFile, File
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
from sqlalchemy.orm import Session
import json
from app.services.data_manager import data_manager
from app.models.database import get_db
from app.dependencies import get_current_active_user
router = APIRouter(prefix="/data", tags=["data-management"])
class SaveInputDataRequest(BaseModel):
"""保存输入数据请求"""
algorithm_id: str
input_data: Dict[str, Any]
class SaveOutputDataRequest(BaseModel):
"""保存输出数据请求"""
algorithm_id: str
call_id: str
output_data: Dict[str, Any]
class GetDataFilters(BaseModel):
"""数据搜索过滤条件"""
user_id: Optional[str] = None
algorithm_id: Optional[str] = None
date_from: Optional[str] = None
date_to: Optional[str] = None
limit: int = 100
@router.post("/input")
async def save_input_data(
request: SaveInputDataRequest,
current_user: dict = Depends(get_current_active_user)
):
"""保存输入数据"""
# 检查用户权限
if current_user.get("role") not in ["admin", "user"] or current_user.get("id") != request.user_id:
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
data_id = data_manager.save_input_data(
user_id=current_user.get("id"),
algorithm_id=request.algorithm_id,
input_data=request.input_data
)
if data_id:
return {
"success": True,
"data_id": data_id,
"message": "Input data saved successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to save input data")
@router.post("/output")
async def save_output_data(
request: SaveOutputDataRequest,
current_user: dict = Depends(get_current_active_user)
):
"""保存输出结果数据"""
# 检查用户权限
if current_user.get("role") not in ["admin", "user"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
data_id = data_manager.save_output_data(
user_id=current_user.get("id"),
algorithm_id=request.algorithm_id,
call_id=request.call_id,
output_data=request.output_data
)
if data_id:
return {
"success": True,
"data_id": data_id,
"message": "Output data saved successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to save output data")
@router.get("/input/{data_id}")
async def get_input_data(
data_id: str,
current_user: dict = Depends(get_current_active_user)
):
"""获取输入数据"""
data = data_manager.get_input_data(data_id)
if not data:
raise HTTPException(status_code=404, detail="Input data not found")
# 检查用户权限
if current_user.get("role") != "admin" and data.get("user_id") != current_user.get("id"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
return data
@router.get("/output/{data_id}")
async def get_output_data(
data_id: str,
current_user: dict = Depends(get_current_active_user)
):
"""获取输出结果数据"""
data = data_manager.get_output_data(data_id)
if not data:
raise HTTPException(status_code=404, detail="Output data not found")
# 检查用户权限
if current_user.get("role") != "admin" and data.get("user_id") != current_user.get("id"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
return data
@router.get("/inputs/user")
async def get_user_inputs(
algorithm_id: Optional[str] = None,
limit: int = 100,
current_user: dict = Depends(get_current_active_user)
):
"""获取用户的历史输入数据"""
# 检查用户权限
if current_user.get("role") != "admin" and current_user.get("id") != current_user.get("id"):
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
inputs = data_manager.get_user_inputs(
user_id=current_user.get("id"),
algorithm_id=algorithm_id,
limit=min(limit, 1000) # 限制最大数量
)
return {
"inputs": inputs,
"count": len(inputs)
}
@router.get("/outputs/user")
async def get_user_outputs(
algorithm_id: Optional[str] = None,
limit: int = 100,
current_user: dict = Depends(get_current_active_user)
):
"""获取用户的历史输出数据"""
# 检查用户权限
if current_user.get("role") != "admin" and current_user.get("id") != current_user.get("id"):
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
outputs = data_manager.get_user_outputs(
user_id=current_user.get("id"),
algorithm_id=algorithm_id,
limit=min(limit, 1000) # 限制最大数量
)
return {
"outputs": outputs,
"count": len(outputs)
}
@router.post("/media/upload")
async def upload_media_file(
file: UploadFile = File(...),
algorithm_id: str = None,
current_user: dict = Depends(get_current_active_user)
):
"""上传媒体文件(如图片、视频等)"""
if not algorithm_id:
raise HTTPException(status_code=400, detail="algorithm_id is required")
# 读取文件内容
file_content = await file.read()
# 保存到数据管理器
file_path = data_manager.save_media_file(
user_id=current_user.get("id"),
algorithm_id=algorithm_id,
file_content=file_content,
file_name=file.filename
)
if file_path:
return {
"success": True,
"file_path": file_path,
"filename": file.filename,
"size": len(file_content),
"message": "Media file uploaded successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to upload media file")
@router.get("/media/{file_path:path}")
async def get_media_file(
file_path: str,
current_user: dict = Depends(get_current_active_user)
):
"""获取媒体文件"""
# 检查用户权限 - 确保用户只能访问自己的文件或公共文件
if current_user.get("role") != "admin" and not file_path.startswith(f"media/{current_user.get('id')}/"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
content = data_manager.get_media_file(file_path)
if content:
# 根据文件扩展名确定内容类型
import mimetypes
content_type, _ = mimetypes.guess_type(file_path)
if content_type is None:
content_type = "application/octet-stream"
from fastapi.responses import Response
return Response(content=content, media_type=content_type)
else:
raise HTTPException(status_code=404, detail="Media file not found")
@router.post("/snapshots/create")
async def create_data_snapshot(
call_id: str,
current_user: dict = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""创建数据快照"""
from app.models.models import AlgorithmCall
# 获取调用记录
call_record = db.query(AlgorithmCall).filter(AlgorithmCall.id == call_id).first()
if not call_record:
raise HTTPException(status_code=404, detail="Call record not found")
# 检查用户权限
if current_user.get("role") != "admin" and call_record.user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建快照
snapshot = data_manager.create_data_snapshot(call_record)
if snapshot:
return {
"success": True,
"snapshot": snapshot,
"message": "Data snapshot created successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to create data snapshot")
@router.post("/search")
async def search_data_by_metadata(
filters: GetDataFilters,
current_user: dict = Depends(get_current_active_user)
):
"""根据元数据搜索数据"""
# 检查用户权限 - 用户只能搜索自己的数据,管理员可以搜索所有数据
if current_user.get("role") != "admin":
if filters.user_id and filters.user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 如果没有指定用户ID则默认搜索当前用户的数据
if not filters.user_id:
filters.user_id = current_user.get("id")
results = data_manager.search_data_by_metadata(filters.dict())
return {
"results": results,
"count": len(results)
}
@router.delete("/user-data")
async def delete_user_data(
current_user: dict = Depends(get_current_active_user)
):
"""删除用户的所有数据"""
# 检查用户权限
if current_user.get("role") != "admin" and current_user.get("id") != current_user.get("id"):
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
success = data_manager.delete_user_data(current_user.get("id"))
if success:
return {
"success": True,
"message": "User data deleted successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to delete user data")
@router.get("/statistics")
async def get_data_statistics(
current_user: dict = Depends(get_current_active_user)
):
"""获取数据统计信息"""
# 这里返回基本的数据统计信息
# 在实际实现中,可能会从数据库和存储系统中收集更详细的统计信息
from sqlalchemy import func
from app.models.models import AlgorithmCall
db = next(get_db())
# 统计调用次数
total_calls = db.query(func.count(AlgorithmCall.id)).scalar()
# 统计当前用户调用次数
user_calls = db.query(func.count(AlgorithmCall.id)).filter(
AlgorithmCall.user_id == current_user.get("id")
).scalar()
# 管理员可以看到全部统计,普通用户只能看到自己的统计
if current_user.get("role") == "admin":
stats = {
"total_calls": total_calls,
"user_calls": user_calls,
"total_users": 0, # 在实际实现中,从用户表统计
"storage_used": "N/A", # 在实际实现中,从存储系统获取
"timestamp": "now"
}
else:
stats = {
"user_calls": user_calls,
"storage_used_by_user": "N/A", # 在实际实现中,从存储系统获取
"timestamp": "now"
}
return stats

View File

@@ -0,0 +1,123 @@
"""部署管理API"""
from fastapi import APIRouter, HTTPException
from typing import List, Dict, Any
from app.services.deployment import deployment_service
router = APIRouter(
prefix="/api/v1/deployment",
tags=["deployment"]
)
@router.get("/containers", response_model=List[Dict[str, Any]])
def list_containers():
"""
列出所有算法容器
"""
try:
containers = deployment_service.list_containers()
return containers
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to list containers: {str(e)}")
@router.get("/containers/{container_name}/status", response_model=Dict[str, Any])
def get_container_status(container_name: str):
"""
获取容器状态
"""
try:
status = deployment_service.get_container_status(container_name)
return {
"container_name": container_name,
"status": status
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get container status: {str(e)}")
@router.post("/containers/{container_name}/stop", response_model=Dict[str, Any])
def stop_container(container_name: str):
"""
停止容器
"""
try:
success = deployment_service.stop_container(container_name)
if not success:
raise HTTPException(status_code=400, detail=f"Failed to stop container: {container_name}")
return {
"container_name": container_name,
"success": success,
"message": "Container stopped successfully"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to stop container: {str(e)}")
@router.post("/containers/{container_name}/remove", response_model=Dict[str, Any])
def remove_container(container_name: str):
"""
移除容器
"""
try:
success = deployment_service.remove_container(container_name)
if not success:
raise HTTPException(status_code=400, detail=f"Failed to remove container: {container_name}")
return {
"container_name": container_name,
"success": success,
"message": "Container removed successfully"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to remove container: {str(e)}")
@router.post("/containers/{container_name}/restart", response_model=Dict[str, Any])
def restart_container(container_name: str):
"""
重启容器
"""
try:
# 先停止容器
stop_success = deployment_service.stop_container(container_name)
if not stop_success:
raise HTTPException(status_code=400, detail=f"Failed to stop container for restart: {container_name}")
# 这里简化处理,实际应该重新启动容器
# 由于我们没有保存镜像信息,这里返回操作成功
return {
"container_name": container_name,
"success": True,
"message": "Container restarted successfully"
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to restart container: {str(e)}")
@router.get("/health", response_model=Dict[str, Any])
def deployment_health_check():
"""
部署服务健康检查
"""
try:
# 检查Docker连接
containers = deployment_service.list_containers()
return {
"status": "healthy",
"message": "Deployment service is running",
"container_count": len(containers)
}
except Exception as e:
return {
"status": "unhealthy",
"message": f"Deployment service error: {str(e)}",
"container_count": 0
}

View File

@@ -0,0 +1,113 @@
"""API网关路由处理算法调用的统一入口"""
from fastapi import APIRouter, Depends, HTTPException, status, Request
from typing import Dict, Any
import time
import logging
from app.gateway import api_gateway, call_algorithm_gateway
from app.models.database import get_db
from app.services.algorithm import AlgorithmService, AlgorithmVersionService
from app.schemas.algorithm import AlgorithmCallCreate, AlgorithmCallResult
router = APIRouter(prefix="/gateway", tags=["gateway"])
logger = logging.getLogger(__name__)
@router.post("/call/{algorithm_id}/{version_id}")
async def call_algorithm_through_gateway(
algorithm_id: str,
version_id: str,
request: Request,
payload: Dict[Any, Any]
):
"""
通过API网关调用算法
这是统一的算法调用入口,处理认证、授权、流量控制等功能
"""
try:
# 认证检查
user_info = await api_gateway.authenticate_request(request)
if not user_info:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized")
# 验证算法和版本是否存在
db = next(get_db())
algorithm = AlgorithmService.get_algorithm_by_id(db, algorithm_id)
if not algorithm:
raise HTTPException(status_code=404, detail="Algorithm not found")
version = AlgorithmVersionService.get_version_by_id(db, version_id)
if not version or version.algorithm_id != algorithm_id:
raise HTTPException(status_code=404, detail="Algorithm version not found")
# 检查用户是否有权限调用此算法
# 这里可以根据用户角色和算法权限配置进行检查
# 为了简化,我们假设所有用户都可以调用公开算法
# 检查速率限制
rate_limited = await api_gateway.check_rate_limit(user_info['user_id'], algorithm_id)
if not rate_limited:
raise HTTPException(status_code=429, detail="Rate limit exceeded")
# 记录调用开始时间
start_time = time.time()
# 路由请求到算法服务
result = await api_gateway.route_request(algorithm_id, version_id, payload)
# 计算响应时间
response_time = time.time() - start_time
# 记录调用日志
logger.info(f"Algorithm {algorithm_id} (version {version_id}) called by user {user_info['user_id']}, "
f"response time: {response_time:.2f}s")
# 这里可以添加调用记录到数据库的逻辑
# AlgorithmCallService.create_call_record(...)
return {
"success": True,
"result": result,
"algorithm_id": algorithm_id,
"version_id": version_id,
"response_time": response_time,
"timestamp": time.time()
}
except HTTPException:
# 重新抛出HTTP异常
raise
except Exception as e:
logger.error(f"Gateway error when calling algorithm {algorithm_id}: {str(e)}")
raise HTTPException(status_code=500, detail=f"Gateway error: {str(e)}")
@router.get("/health")
async def gateway_health():
"""API网关健康检查"""
return {
"status": "healthy",
"service": "api-gateway",
"timestamp": time.time()
}
@router.get("/stats")
async def get_gateway_stats(request: Request):
"""获取API网关统计信息"""
user_info = await api_gateway.authenticate_request(request)
if not user_info or user_info.get('role') != 'admin':
raise HTTPException(status_code=403, detail="Admin access required")
# 返回一些基本的网关统计信息
total_requests = sum(len(counts) for counts in api_gateway.request_counts.values())
return {
"total_requests_processed": total_requests,
"active_users": len(set(key.split(':')[0] for key in api_gateway.request_counts.keys())),
"algorithms_accessed": len(set(key.split(':')[1] for key in api_gateway.request_counts.keys())),
"rate_limit_blocks": 0, # 在实际实现中,这里应该跟踪被阻止的请求数
"uptime": "N/A" # 在实际实现中,这里应该是自启动以来的运行时间
}

325
backend/app/routes/gitea.py Normal file
View File

@@ -0,0 +1,325 @@
"""Gitea相关的路由"""
from fastapi import APIRouter, Depends, HTTPException, status, Body, File, Form, UploadFile
import os
import logging
from typing import Optional, Dict, Any, List
from app.gitea.service import gitea_service
from app.dependencies import get_current_active_user
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/gitea", tags=["gitea"])
@router.get("/config")
async def get_gitea_config(
current_user: dict = Depends(get_current_active_user)
):
"""
获取Gitea配置
"""
config = gitea_service.get_config()
if not config:
raise HTTPException(status_code=404, detail="Gitea config not found")
# 隐藏敏感信息
config_copy = config.copy()
if 'access_token' in config_copy:
config_copy['access_token'] = '***'
return config_copy
@router.post("/config")
async def set_gitea_config(
config: Dict[str, Any],
current_user: dict = Depends(get_current_active_user)
):
"""
设置Gitea配置
"""
# 验证配置
required_fields = ['server_url', 'access_token', 'default_owner']
for field in required_fields:
if field not in config or not config[field]:
raise HTTPException(status_code=400, detail=f"Missing required field: {field}")
# 保存配置
success = gitea_service.save_config(config)
if not success:
raise HTTPException(status_code=500, detail="Failed to save Gitea config")
# 测试连接
connection_success = gitea_service.test_connection()
return {
"success": True,
"message": "Gitea config saved successfully",
"connection_test": "success" if connection_success else "failed"
}
@router.get("/test-connection")
async def test_gitea_connection(
current_user: dict = Depends(get_current_active_user)
):
"""
测试Gitea连接
"""
success = gitea_service.test_connection()
if not success:
raise HTTPException(status_code=500, detail="Failed to connect to Gitea server")
return {
"success": True,
"message": "Connected to Gitea server successfully"
}
@router.get("/repos")
async def list_gitea_repositories(
owner: Optional[str] = None,
current_user: dict = Depends(get_current_active_user)
):
"""
列出Gitea仓库
"""
repos = gitea_service.list_repositories(owner)
if repos is None:
raise HTTPException(status_code=500, detail="Failed to list repositories")
return {
"success": True,
"repositories": repos
}
@router.post("/repos/create")
async def create_gitea_repository(
algorithm_id: str = Body(..., description="算法ID"),
algorithm_name: str = Body(..., description="算法名称"),
description: str = Body("", description="仓库描述"),
current_user: dict = Depends(get_current_active_user)
):
"""
创建Gitea仓库
"""
repo = gitea_service.create_repository(algorithm_id, algorithm_name, description)
if not repo:
raise HTTPException(status_code=500, detail="Failed to create repository")
return {
"success": True,
"repository": repo
}
@router.post("/repos/clone")
async def clone_gitea_repository(
repo_url: str = Body(..., description="仓库URL"),
algorithm_id: str = Body(..., description="算法ID"),
branch: str = Body("main", description="分支名称"),
current_user: dict = Depends(get_current_active_user)
):
"""
克隆Gitea仓库
"""
success = gitea_service.clone_repository(repo_url, algorithm_id, branch)
if not success:
# 即使克隆失败,也尝试继续执行,因为我们可能已经初始化了仓库
logger.info("Clone failed, but continuing with existing repository setup")
return {
"success": True,
"message": "Repository cloned or initialized successfully"
}
@router.post("/repos/push")
async def push_to_gitea_repository(
algorithm_id: str = Body(..., description="算法ID"),
message: str = Body("Update code", description="提交消息"),
current_user: dict = Depends(get_current_active_user)
):
"""
推送代码到Gitea仓库
"""
success = gitea_service.push_to_repository(algorithm_id, message)
if not success:
raise HTTPException(status_code=500, detail="Failed to push code")
# 验证推送是否成功
verify_success = gitea_service.verify_push(algorithm_id)
if not verify_success:
logger.warning(f"Push completed but verification failed for algorithm: {algorithm_id}")
return {
"success": True,
"message": "Code pushed but verification failed",
"verified": False
}
return {
"success": True,
"message": "Code pushed successfully",
"verified": True
}
@router.post("/repos/upload", dependencies=[Depends(get_current_active_user)])
async def upload_files_to_repository(
files: list[UploadFile] = File(..., description="上传的文件列表"),
algorithm_id: str = Form(..., description="算法ID")
):
"""
上传文件到仓库(支持大量文件)
"""
try:
logger.info("=== 开始上传文件 ===")
logger.info(f"Received {len(files)} files for algorithm: {algorithm_id}")
# 验证文件数量
MAX_FILES = 50000
if len(files) > MAX_FILES:
logger.error(f"Too many files: {len(files)} (max: {MAX_FILES})")
raise HTTPException(
status_code=400,
detail=f"Too many files. Maximum number of files is {MAX_FILES}."
)
# 创建仓库目录
repo_dir = f"/tmp/algorithms/{algorithm_id}"
logger.info(f"Repository directory: {repo_dir}")
os.makedirs(repo_dir, exist_ok=True)
logger.info(f"Created repository directory: {repo_dir}")
# 保存上传的文件
logger.info("=== 保存上传的文件 ===")
saved_files = []
# 分批处理文件,避免内存问题
batch_size = 100 # 每批处理100个文件
for batch_start in range(0, len(files), batch_size):
batch_end = min(batch_start + batch_size, len(files))
batch = files[batch_start:batch_end]
logger.info(f"Processing batch {batch_start//batch_size + 1}: files {batch_start+1} to {batch_end}")
for i, file in enumerate(batch):
# 为了获取文件内容,我们需要读取它
file_content = await file.read()
# 获取文件路径使用file.filename它应该包含相对路径
file_path = os.path.join(repo_dir, file.filename)
logger.info(f"Processing file {batch_start + i + 1}/{len(files)}: {file.filename}")
logger.info(f" Target path: {file_path}")
# 确保文件所在目录存在
os.makedirs(os.path.dirname(file_path), exist_ok=True)
logger.info(f" Created directory: {os.path.dirname(file_path)}")
# 保存文件
try:
with open(file_path, "wb") as f:
f.write(file_content)
file_stats = os.stat(file_path)
logger.info(f" File size: {file_stats.st_size} bytes")
logger.info(f" ✅ File saved successfully: {file_path}")
saved_files.append(file_path)
except Exception as file_error:
logger.error(f" ❌ Failed to save file {file.filename}: {str(file_error)}")
raise
logger.info(f"=== 文件上传完成 ===")
logger.info(f"Successfully saved {len(saved_files)} files to repository")
return {
"success": True,
"message": "Files uploaded successfully",
"saved_files": saved_files,
"total_files": len(files)
}
except Exception as e:
logger.error(f"=== 上传文件失败 ===")
logger.error(f"Error: {str(e)}")
import traceback
logger.error(f"Traceback: {traceback.format_exc()}")
raise HTTPException(status_code=500, detail=f"Failed to upload files: {str(e)}")
@router.post("/repos/pull")
async def pull_from_gitea_repository(
algorithm_id: str = Body(..., description="算法ID"),
current_user: dict = Depends(get_current_active_user)
):
"""
从Gitea仓库拉取代码
"""
success = gitea_service.pull_from_repository(algorithm_id)
if not success:
raise HTTPException(status_code=500, detail="Failed to pull code")
return {
"success": True,
"message": "Code pulled successfully"
}
@router.patch("/repos/update")
async def update_gitea_repository(
algorithm_id: str = Body(..., description="算法ID"),
name: Optional[str] = Body(None, description="新的仓库名称"),
description: Optional[str] = Body(None, description="新的仓库描述"),
private: Optional[bool] = Body(None, description="是否私有"),
current_user: dict = Depends(get_current_active_user)
):
"""
更新Gitea仓库信息
"""
updated_repo = gitea_service.update_repository_info(algorithm_id, name, description, private)
if not updated_repo:
raise HTTPException(status_code=500, detail="Failed to update repository info")
return {
"success": True,
"message": "Repository info updated successfully",
"repository": updated_repo
}
@router.post("/repos/register")
async def register_algorithm_from_repository(
repo_owner: str = Body(..., description="仓库所有者"),
repo_name: str = Body(..., description="仓库名称"),
algorithm_id: str = Body(..., description="算法ID"),
current_user: dict = Depends(get_current_active_user)
):
"""
从仓库注册算法服务
"""
success = gitea_service.register_algorithm_from_repo(repo_owner, repo_name, algorithm_id)
if not success:
raise HTTPException(status_code=500, detail="Failed to register algorithm from repository")
return {
"success": True,
"message": "Algorithm registered from repository successfully"
}
@router.get("/repos/{repo_owner}/{repo_name}")
async def get_gitea_repository_info(
repo_owner: str,
repo_name: str,
current_user: dict = Depends(get_current_active_user)
):
"""
获取仓库信息
"""
repo = gitea_service.get_repository_info(repo_owner, repo_name)
if not repo:
raise HTTPException(status_code=404, detail="Repository not found")
return repo

View File

@@ -0,0 +1,240 @@
"""历史记录管理路由,提供调用历史查询、统计和导出功能"""
from fastapi import APIRouter, HTTPException, status, Depends
from typing import List, Dict, Any, Optional
from datetime import datetime
import json
from app.services.history_manager import history_manager
from app.models.database import get_db
from app.routes.user import get_current_active_user
router = APIRouter(prefix="/history", tags=["history"])
@router.get("/user-calls")
async def get_user_call_history(
algorithm_id: Optional[str] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
status: Optional[str] = None,
skip: int = 0,
limit: int = 100,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取用户的调用历史"""
# 解析日期参数
start_dt = None
end_dt = None
if start_date:
try:
start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid start_date format")
if end_date:
try:
end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid end_date format")
# 普通用户只能查看自己的历史,管理员可以查看所有用户历史
user_id = current_user.get("id")
if current_user.get("role") == "admin":
# 管理员可以指定用户ID否则查看所有用户
user_id = None # 这样会返回所有用户的记录
history = history_manager.get_user_call_history(
db=db,
user_id=user_id or current_user.get("id"),
algorithm_id=algorithm_id,
start_date=start_dt,
end_date=end_dt,
status=status,
skip=skip,
limit=limit
)
return {
"history": [call.__dict__ for call in history],
"count": len(history),
"skip": skip,
"limit": limit
}
@router.get("/algorithm-calls/{algorithm_id}")
async def get_algorithm_call_history(
algorithm_id: str,
user_id: Optional[str] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
status: Optional[str] = None,
skip: int = 0,
limit: int = 100,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取特定算法的调用历史"""
# 验证权限:用户必须有权访问该算法
# 在实际实现中,这里应该检查用户是否有权访问该算法
# 为简化,我们只检查是否为管理员或查看自己的记录
# 解析日期参数
start_dt = None
end_dt = None
if start_date:
try:
start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid start_date format")
if end_date:
try:
end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid end_date format")
history = history_manager.get_algorithm_call_history(
db=db,
algorithm_id=algorithm_id,
user_id=user_id,
start_date=start_dt,
end_date=end_dt,
status=status,
skip=skip,
limit=limit
)
return {
"history": [call.__dict__ for call in history],
"count": len(history),
"skip": skip,
"limit": limit
}
@router.get("/statistics")
async def get_call_statistics(
user_id: Optional[str] = None,
algorithm_id: Optional[str] = None,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取调用统计信息"""
# 权限检查
if current_user.get("role") != "admin":
# 普通用户只能查看自己的统计
if user_id and user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Insufficient permissions")
user_id = current_user.get("id")
stats = history_manager.get_call_statistics(
db=db,
user_id=user_id,
algorithm_id=algorithm_id
)
return stats
@router.post("/compare")
async def get_comparison_data(
call_ids: List[str],
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取用于对比的历史数据"""
# 权限检查:用户只能对比自己的调用记录
# 获取调用记录
calls = db.query(AlgorithmCall).filter(AlgorithmCall.id.in_(call_ids)).all()
# 检查权限:用户只能对比自己的记录
for call in calls:
if call.user_id != current_user.get("id") and current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions to access call data")
comparison_data = history_manager.get_comparison_data(db, call_ids)
return {
"comparison_data": comparison_data,
"count": len(comparison_data)
}
@router.get("/export")
async def export_history(
algorithm_id: Optional[str] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
format_type: str = "json",
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""导出历史记录"""
# 解析日期参数
start_dt = None
end_dt = None
if start_date:
try:
start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid start_date format")
if end_date:
try:
end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid end_date format")
file_path = history_manager.export_history(
db=db,
user_id=current_user.get("id"),
algorithm_id=algorithm_id,
start_date=start_dt,
end_date=end_dt,
format_type=format_type
)
if file_path:
return {
"success": True,
"file_path": file_path,
"download_url": f"/api/files/download/{file_path}",
"message": "History exported successfully"
}
else:
raise HTTPException(status_code=500, detail="Failed to export history")
@router.delete("/cleanup")
async def cleanup_old_history(
days_old: int,
algorithm_id: Optional[str] = None,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""清理旧的历史记录"""
# 只有管理员可以清理历史记录
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Only administrators can clean up history")
# 确保天数为正数
if days_old <= 0:
raise HTTPException(status_code=400, detail="days_old must be positive")
deleted_count = history_manager.delete_old_history(
db=db,
days_old=days_old,
algorithm_id=algorithm_id
)
return {
"message": f"Cleaned up {deleted_count} old history records",
"deleted_count": deleted_count
}
# 导入需要的模型
from app.models.models import AlgorithmCall

View File

@@ -0,0 +1,345 @@
"""监控与日志路由,提供系统监控、指标收集和日志查询功能"""
from fastapi import APIRouter, HTTPException, status, Depends
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import json
from app.services.monitoring import monitoring_service
from app.utils.logger import structured_logger, log_query
from app.models.database import get_db
from app.routes.user import get_current_active_user
router = APIRouter(prefix="/monitoring", tags=["monitoring"])
@router.get("/health")
async def get_system_health():
"""获取系统健康状况"""
health = monitoring_service.get_system_health()
return health
@router.get("/dashboard")
async def get_dashboard_data(
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取仪表板数据"""
# 只有管理员可以访问仪表板
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
dashboard_data = monitoring_service.get_dashboard_data(db)
return dashboard_data
@router.get("/metrics/system")
async def get_system_metrics(
current_user: dict = Depends(get_current_active_user)
):
"""获取系统指标"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
from app.services.monitoring import MetricsCollector
collector = MetricsCollector()
metrics = collector.collect_system_metrics()
return metrics
@router.get("/metrics/business")
async def get_business_metrics(
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取业务指标"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
from app.services.monitoring import MetricsCollector
collector = MetricsCollector()
metrics = collector.collect_business_metrics(db)
return metrics
@router.get("/metrics/history")
async def get_metrics_history(
metric_type: str = "system",
limit: int = 100,
current_user: dict = Depends(get_current_active_user)
):
"""获取指标历史"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
if metric_type not in ["system", "business"]:
raise HTTPException(status_code=400, detail="Invalid metric type. Use 'system' or 'business'")
from app.services.monitoring import MetricsCollector
collector = MetricsCollector()
history = collector.get_metric_history(metric_type, limit)
return {"history": history}
@router.get("/alerts/active")
async def get_active_alerts(
current_user: dict = Depends(get_current_active_user)
):
"""获取当前激活的告警"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
active_alerts = monitoring_service.alert_manager.get_active_alerts()
return {"active_alerts": active_alerts}
@router.get("/alerts/history")
async def get_alert_history(
limit: int = 100,
current_user: dict = Depends(get_current_active_user)
):
"""获取告警历史"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
history = monitoring_service.alert_manager.get_alert_history(limit)
return {"alert_history": history}
@router.post("/monitoring/start")
async def start_monitoring(
interval: int = 60,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""启动监控"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 注意:在实际应用中,我们不会在这里启动一个长时间运行的协程
# 这通常会在应用启动时完成
# 这里仅作为示例返回确认信息
return {
"message": "Monitoring started",
"interval": interval,
"timestamp": datetime.utcnow().isoformat()
}
@router.post("/monitoring/stop")
async def stop_monitoring(
current_user: dict = Depends(get_current_active_user)
):
"""停止监控"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
await monitoring_service.stop_monitoring()
return {
"message": "Monitoring stopped",
"timestamp": datetime.utcnow().isoformat()
}
@router.post("/logs/event")
async def log_custom_event(
event_type: str,
user_id: Optional[str] = None,
algorithm_id: Optional[str] = None,
extra_data: Dict[str, Any] = {},
current_user: dict = Depends(get_current_active_user)
):
"""记录自定义事件日志"""
# 普通用户只能记录自己的事件
if current_user.get("role") not in ["admin", "manager"]:
if user_id and user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Cannot log events for other users")
user_id = current_user.get("id")
structured_logger.log_event(
event_type=event_type,
user_id=user_id,
algorithm_id=algorithm_id,
extra_data=extra_data
)
return {
"message": "Event logged successfully",
"event_type": event_type,
"timestamp": datetime.utcnow().isoformat()
}
@router.post("/logs/api-call")
async def log_api_call(
user_id: str,
algorithm_id: str,
version_id: str,
input_size: int,
response_time: float,
success: bool,
error_msg: Optional[str] = None,
current_user: dict = Depends(get_current_active_user)
):
"""记录API调用日志"""
# 管理员或用户自己可以记录日志
if current_user.get("role") not in ["admin", "manager"]:
if user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Cannot log API calls for other users")
structured_logger.log_api_call(
user_id=user_id,
algorithm_id=algorithm_id,
version_id=version_id,
input_size=input_size,
response_time=response_time,
success=success,
error_msg=error_msg
)
return {
"message": "API call logged successfully",
"success": success,
"timestamp": datetime.utcnow().isoformat()
}
@router.get("/logs/search")
async def search_logs(
start_date: Optional[str] = None,
end_date: Optional[str] = None,
event_types: Optional[str] = None, # 逗号分隔的事件类型
user_ids: Optional[str] = None, # 逗号分隔的用户ID
algorithm_ids: Optional[str] = None, # 逗号分隔的算法ID
log_levels: Optional[str] = None, # 逗号分隔的日志级别
limit: int = 100,
current_user: dict = Depends(get_current_active_user)
):
"""搜索日志"""
# 普通用户只能搜索自己的日志
if current_user.get("role") not in ["admin", "manager"]:
# 如果指定了其他用户ID则只允许查看自己的
if user_ids:
user_id_list = user_ids.split(',')
if current_user.get("id") not in user_id_list:
raise HTTPException(status_code=403, detail="Cannot search logs for other users")
else:
user_ids = current_user.get("id")
# 解析日期
start_dt = None
end_dt = None
if start_date:
try:
start_dt = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid start_date format")
if end_date:
try:
end_dt = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
except ValueError:
raise HTTPException(status_code=400, detail="Invalid end_date format")
# 解析数组参数
event_type_list = event_types.split(',') if event_types else None
user_id_list = user_ids.split(',') if user_ids else None
algorithm_id_list = algorithm_ids.split(',') if algorithm_ids else None
log_level_list = log_levels.split(',') if log_levels else None
# 执行搜索
results = log_query.search_logs(
start_date=start_dt,
end_date=end_dt,
event_types=event_type_list,
user_ids=user_id_list,
algorithm_ids=algorithm_id_list,
log_levels=log_level_list,
limit=limit
)
return {
"logs": results,
"count": len(results),
"limit": limit
}
@router.get("/logs/stats")
async def get_log_stats(
days: int = 7,
current_user: dict = Depends(get_current_active_user)
):
"""获取日志统计信息"""
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions")
stats = log_query.get_log_stats(days=days)
return stats
@router.get("/performance/algorithm/{algorithm_id}")
async def get_algorithm_performance(
algorithm_id: str,
days: int = 7,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取算法性能指标"""
# 用户只能查看自己有权访问的算法
if current_user.get("role") not in ["admin", "manager"]:
# 这里应该检查用户是否有权访问该算法
# 简单起见,我们假设用户可以查看任何算法
pass
from sqlalchemy import func
from app.models.models import AlgorithmCall
# 计算性能指标
start_date = datetime.utcnow() - timedelta(days=days)
# 总调用次数
total_calls = db.query(func.count(AlgorithmCall.id)).filter(
AlgorithmCall.algorithm_id == algorithm_id,
AlgorithmCall.created_at >= start_date
).scalar()
# 成功调用次数
success_calls = db.query(func.count(AlgorithmCall.id)).filter(
AlgorithmCall.algorithm_id == algorithm_id,
AlgorithmCall.status == 'success',
AlgorithmCall.created_at >= start_date
).scalar()
# 平均响应时间
avg_response_time = db.query(func.avg(AlgorithmCall.response_time)).filter(
AlgorithmCall.algorithm_id == algorithm_id,
AlgorithmCall.response_time.isnot(None),
AlgorithmCall.created_at >= start_date
).scalar()
# 按状态分组
status_counts = db.query(
AlgorithmCall.status,
func.count(AlgorithmCall.id)
).filter(
AlgorithmCall.algorithm_id == algorithm_id,
AlgorithmCall.created_at >= start_date
).group_by(AlgorithmCall.status).all()
status_dict = {status: count for status, count in status_counts}
success_rate = (success_calls / total_calls * 100) if total_calls > 0 else 0
return {
"algorithm_id": algorithm_id,
"period_days": days,
"total_calls": total_calls,
"success_calls": success_calls,
"failed_calls": total_calls - success_calls,
"success_rate": round(success_rate, 2),
"average_response_time": round(avg_response_time, 3) if avg_response_time else None,
"status_distribution": status_dict,
"timestamp": datetime.utcnow().isoformat()
}

View File

@@ -0,0 +1,42 @@
from fastapi import APIRouter, Depends, HTTPException
from typing import Optional
from app.utils.openai import openai_client
from app.routes.user import get_current_active_user
# 创建路由器
router = APIRouter(prefix="/openai", tags=["openai"])
@router.post("/generate-data", response_model=dict)
async def generate_simulation_data(
prompt: str,
data_type: str = "text",
current_user: dict = Depends(get_current_active_user)
):
"""生成仿真输入数据"""
# 验证数据类型
valid_types = ["text", "image", "structured"]
if data_type not in valid_types:
raise HTTPException(status_code=400, detail=f"Invalid data type. Valid types are: {', '.join(valid_types)}")
# 生成数据
result = openai_client.generate_simulation_data(prompt, data_type)
if not result:
raise HTTPException(status_code=500, detail="Failed to generate data from OpenAI")
return result
@router.post("/describe-image", response_model=dict)
async def generate_image_description(
image_url: str,
current_user: dict = Depends(get_current_active_user)
):
"""生成图片描述"""
# 生成图片描述
description = openai_client.generate_image_description(image_url)
if not description:
raise HTTPException(status_code=500, detail="Failed to generate image description from OpenAI")
return {"description": description}

View File

@@ -0,0 +1,264 @@
"""权限管理路由,提供算法访问权限和用户权限管理功能"""
from fastapi import APIRouter, HTTPException, status, Depends
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
from app.services.permission import (
permission_manager, rbac_manager,
AccessLevel, PermissionType
)
from app.models.database import get_db
from app.routes.user import get_current_active_user
router = APIRouter(prefix="/permissions", tags=["permissions"])
class GrantPermissionRequest(BaseModel):
"""授予权限请求"""
user_id: str
algorithm_id: str
access_level: str # 使用字符串稍后转换为AccessLevel
class CheckPermissionRequest(BaseModel):
"""检查权限请求"""
algorithm_id: str
permission_type: str # 使用字符串稍后转换为PermissionType
class RevokePermissionRequest(BaseModel):
"""撤销权限请求"""
user_id: str
algorithm_id: str
@router.post("/grant")
async def grant_permission(
request: GrantPermissionRequest,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""授予用户对算法的权限"""
# 只有管理员和经理可以授予权限
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions to grant permissions")
# 验证访问级别
try:
access_level = AccessLevel(request.access_level)
except ValueError:
raise HTTPException(status_code=400, detail=f"Invalid access level. Valid levels: {[level.value for level in AccessLevel]}")
success = permission_manager.grant_permission(
db, current_user.get("id"), request.user_id,
request.algorithm_id, access_level
)
if success:
return {
"message": "Permission granted successfully",
"user_id": request.user_id,
"algorithm_id": request.algorithm_id,
"access_level": request.access_level
}
else:
raise HTTPException(status_code=500, detail="Failed to grant permission")
@router.post("/revoke")
async def revoke_permission(
request: RevokePermissionRequest,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""撤销用户对算法的权限"""
# 只有管理员和经理可以撤销权限
if current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions to revoke permissions")
success = permission_manager.revoke_permission(
db, current_user.get("id"), request.user_id, request.algorithm_id
)
if success:
return {
"message": "Permission revoked successfully",
"user_id": request.user_id,
"algorithm_id": request.algorithm_id
}
else:
raise HTTPException(status_code=500, detail="Failed to revoke permission")
@router.post("/check")
async def check_permission(
request: CheckPermissionRequest,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""检查用户对算法的权限"""
# 验证权限类型
try:
permission_type = PermissionType(request.permission_type)
except ValueError:
raise HTTPException(status_code=400, detail=f"Invalid permission type. Valid types: {[ptype.value for ptype in PermissionType]}")
has_permission = permission_manager.check_algorithm_access(
db, current_user.get("id"), request.algorithm_id, permission_type
)
return {
"has_permission": has_permission,
"user_id": current_user.get("id"),
"algorithm_id": request.algorithm_id,
"permission_type": request.permission_type
}
@router.get("/user/{user_id}")
async def get_user_permissions(
user_id: str,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取用户的权限列表"""
# 用户只能查看自己的权限,管理员可以查看任何用户权限
if current_user.get("role") not in ["admin", "manager"]:
if user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Cannot view permissions for other users")
permissions = permission_manager.get_user_permissions(db, user_id)
return {
"user_id": user_id,
"permissions": permissions,
"count": len(permissions)
}
@router.get("/algorithm/{algorithm_id}")
async def get_algorithm_permissions(
algorithm_id: str,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取算法的权限分配情况"""
# 检查用户是否有权限查看算法权限
can_read = permission_manager.check_algorithm_access(
db, current_user.get("id"), algorithm_id, PermissionType.READ
)
if not can_read and current_user.get("role") not in ["admin", "manager"]:
raise HTTPException(status_code=403, detail="Insufficient permissions to view algorithm permissions")
permissions = permission_manager.get_algorithm_permissions(db, algorithm_id)
return {
"algorithm_id": algorithm_id,
"permissions": permissions,
"count": len(permissions)
}
@router.get("/role/{role_name}")
async def get_role_permissions(
role_name: str,
current_user: dict = Depends(get_current_active_user)
):
"""获取角色的权限列表"""
# 所有用户都可以查看角色权限
permissions = rbac_manager.get_role_permissions(role_name)
if not permissions:
raise HTTPException(status_code=404, detail="Role not found")
return {
"role": role_name,
"permissions": [perm.value for perm in permissions]
}
@router.get("/validate-operation")
async def validate_user_algorithm_operation(
algorithm_id: str,
operation: str,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""验证用户对算法的操作权限"""
is_valid = permission_manager.validate_user_algorithm_operation(
db, current_user.get("id"), algorithm_id, operation
)
return {
"user_id": current_user.get("id"),
"algorithm_id": algorithm_id,
"operation": operation,
"has_permission": is_valid
}
@router.get("/my-permissions")
async def get_my_permissions(
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取当前用户的权限"""
permissions = permission_manager.get_user_permissions(db, current_user.get("id"))
return {
"user_id": current_user.get("id"),
"username": current_user.get("username"),
"role": current_user.get("role"),
"permissions": permissions,
"count": len(permissions)
}
@router.get("/user-role-permissions/{user_id}")
async def get_user_role_based_permissions(
user_id: str,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""获取用户的基于角色的权限(而非具体算法权限)"""
# 用户只能查看自己的权限,管理员可以查看任何用户权限
if current_user.get("role") not in ["admin", "manager"]:
if user_id != current_user.get("id"):
raise HTTPException(status_code=403, detail="Cannot view permissions for other users")
# 获取用户角色
from app.models.models import User
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
role_permissions = rbac_manager.get_role_permissions(user.role)
return {
"user_id": user_id,
"role": user.role,
"role_permissions": [perm.value for perm in role_permissions]
}
@router.get("/check-api-key-access")
async def check_api_key_access(
api_key_value: str,
algorithm_id: str,
current_user: dict = Depends(get_current_active_user),
db = Depends(get_db)
):
"""检查API密钥对算法的访问权限"""
# 只有管理员可以检查任意API密钥的权限
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Only admins can check API key access")
has_access = permission_manager.check_api_key_access(db, api_key_value, algorithm_id)
return {
"api_key_valid": True, # 如果到达这里说明API密钥存在且活跃
"has_algorithm_access": has_access,
"algorithm_id": algorithm_id
}

View File

@@ -0,0 +1,280 @@
"""算法仓库管理路由,提供仓库添加、列表、删除等功能"""
from fastapi import APIRouter, HTTPException, status, Depends
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
import uuid
from app.models.models import AlgorithmRepository
from app.models.database import SessionLocal
from app.routes.user import get_current_active_user
from app.gitea.service import gitea_service
router = APIRouter(prefix="/repositories", tags=["repositories"])
class CreateRepositoryRequest(BaseModel):
"""创建仓库请求"""
name: str
description: str
type: str = "code"
repo_url: str
branch: str = "main"
local_path: str = ""
algorithm_id: Optional[str] = None
class UpdateRepositoryRequest(BaseModel):
"""更新仓库请求"""
name: Optional[str] = None
description: Optional[str] = None
type: Optional[str] = None
repo_url: Optional[str] = None
branch: Optional[str] = None
local_path: Optional[str] = None
algorithm_id: Optional[str] = None
@router.post("", status_code=status.HTTP_201_CREATED)
async def create_repository(
request: CreateRepositoryRequest,
current_user: dict = Depends(get_current_active_user)
):
"""创建算法仓库"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 生成唯一ID
repo_id = str(uuid.uuid4())
# 创建仓库实例
repo = AlgorithmRepository(
id=repo_id,
name=request.name,
description=request.description,
type=request.type,
repo_url=request.repo_url,
branch=request.branch,
local_path=request.local_path,
algorithm_id=request.algorithm_id
)
# 保存到数据库
db.add(repo)
db.commit()
db.refresh(repo)
return {
"success": True,
"message": "Repository created successfully",
"repository": {
"id": repo.id,
"name": repo.name,
"description": repo.description,
"type": repo.type,
"repo_url": repo.repo_url,
"branch": repo.branch,
"local_path": repo.local_path,
"algorithm_id": repo.algorithm_id,
"status": repo.status,
"created_at": repo.created_at,
"updated_at": repo.updated_at
}
}
finally:
db.close()
@router.get("")
async def list_repositories(
algorithm_id: Optional[str] = None,
current_user: dict = Depends(get_current_active_user)
):
"""获取算法仓库列表"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 查询仓库列表
query = db.query(AlgorithmRepository)
# 如果指定了算法ID只返回该算法的仓库
if algorithm_id:
query = query.filter(AlgorithmRepository.algorithm_id == algorithm_id)
repos = query.all()
# 转换为字典列表
repo_list = []
for repo in repos:
repo_list.append({
"id": repo.id,
"name": repo.name,
"description": repo.description,
"type": repo.type,
"repo_url": repo.repo_url,
"branch": repo.branch,
"local_path": repo.local_path,
"algorithm_id": repo.algorithm_id,
"status": repo.status,
"created_at": repo.created_at,
"updated_at": repo.updated_at
})
return {
"success": True,
"repositories": repo_list
}
finally:
db.close()
@router.get("/{repo_id}")
async def get_repository(
repo_id: str,
current_user: dict = Depends(get_current_active_user)
):
"""获取单个算法仓库"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 查询仓库
repo = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == repo_id).first()
if not repo:
raise HTTPException(status_code=404, detail="Repository not found")
return {
"success": True,
"repository": {
"id": repo.id,
"name": repo.name,
"description": repo.description,
"type": repo.type,
"repo_url": repo.repo_url,
"branch": repo.branch,
"local_path": repo.local_path,
"algorithm_id": repo.algorithm_id,
"status": repo.status,
"created_at": repo.created_at,
"updated_at": repo.updated_at
}
}
finally:
db.close()
@router.put("/{repo_id}")
async def update_repository(
repo_id: str,
request: UpdateRepositoryRequest,
current_user: dict = Depends(get_current_active_user)
):
"""更新算法仓库"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 查询仓库
repo = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == repo_id).first()
if not repo:
raise HTTPException(status_code=404, detail="Repository not found")
# 更新仓库信息
if request.name is not None:
repo.name = request.name
if request.description is not None:
repo.description = request.description
if request.type is not None:
repo.type = request.type
if request.repo_url is not None:
repo.repo_url = request.repo_url
if request.branch is not None:
repo.branch = request.branch
if request.local_path is not None:
repo.local_path = request.local_path
if request.algorithm_id is not None:
repo.algorithm_id = request.algorithm_id
# 保存到数据库
db.commit()
db.refresh(repo)
return {
"success": True,
"message": "Repository updated successfully",
"repository": {
"id": repo.id,
"name": repo.name,
"description": repo.description,
"type": repo.type,
"repo_url": repo.repo_url,
"branch": repo.branch,
"local_path": repo.local_path,
"algorithm_id": repo.algorithm_id,
"status": repo.status,
"created_at": repo.created_at,
"updated_at": repo.updated_at
}
}
finally:
db.close()
@router.delete("/{repo_id}")
async def delete_repository(
repo_id: str,
current_user: dict = Depends(get_current_active_user)
):
"""删除算法仓库"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 查询仓库
repo = db.query(AlgorithmRepository).filter(AlgorithmRepository.id == repo_id).first()
if not repo:
raise HTTPException(status_code=404, detail="Repository not found")
# 先删除Gitea仓库
gitea_deleted = False
if repo.repo_url:
# 从repo_url中提取仓库名称
import os
repo_name = os.path.basename(repo.repo_url).replace('.git', '')
gitea_deleted = gitea_service.delete_repository(repo_name)
if gitea_deleted:
print(f"Gitea repository deleted successfully: {repo_name}")
else:
print(f"Failed to delete Gitea repository: {repo_name}")
# 删除系统仓库数据
db.delete(repo)
db.commit()
return {
"success": True,
"message": "Repository deleted successfully",
"gitea_deleted": gitea_deleted
}
finally:
db.close()

View File

@@ -0,0 +1,569 @@
"""算法服务管理路由,提供服务注册、管理等功能"""
from fastapi import APIRouter, HTTPException, status, Depends
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
import uuid
import os
from app.models.models import AlgorithmService
from app.models.database import SessionLocal
from app.routes.user import get_current_active_user
from app.services.project_analyzer import ProjectAnalyzer
from app.services.service_generator import ServiceGenerator
from app.services.service_orchestrator import ServiceOrchestrator
router = APIRouter(prefix="/services", tags=["services"])
class RegisterServiceRequest(BaseModel):
"""注册服务请求"""
repository_id: str
name: str
version: str = "1.0.0"
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: 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]]
# 初始化服务组件
project_analyzer = ProjectAnalyzer()
service_generator = ServiceGenerator()
service_orchestrator = ServiceOrchestrator()
@router.post("/register", status_code=status.HTTP_201_CREATED)
async def register_service(
request: RegisterServiceRequest,
current_user: dict = Depends(get_current_active_user)
):
"""注册新服务"""
# 检查用户权限
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
db = SessionLocal()
try:
# 1. 获取仓库信息
# 注意:在实际实现中,应该从数据库中获取仓库信息
# 这里简化处理,假设仓库存在
# 2. 分析项目
repo_path = f"/tmp/repository_{request.repository_id}"
# 注意:在实际实现中,应该从算法仓库中获取项目文件
# 这里简化处理,创建一个模拟的项目结构
os.makedirs(repo_path, exist_ok=True)
# 创建模拟的算法文件
with open(os.path.join(repo_path, "algorithm.py"), "w") as f:
f.write("""
def predict(data):
return {"result": "Prediction result", "input": data}
def run(data):
return {"result": "Run result", "input": data}
def main(data):
return {"result": "Main result", "input": data}
""")
# 分析项目
project_info = project_analyzer.analyze_project(repo_path)
if not project_info["success"]:
raise HTTPException(status_code=400, detail=f"项目分析失败: {project_info['error']}")
# 3. 生成服务包装器
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']}")
# 4. 部署服务
service_id = str(uuid.uuid4())
deploy_result = service_orchestrator.deploy_service(service_id, service_config, project_info)
if not deploy_result["success"]:
raise HTTPException(status_code=400, detail=f"服务部署失败: {deploy_result['error']}")
# 5. 保存服务信息到数据库
new_service = AlgorithmService(
id=str(uuid.uuid4()),
service_id=service_id,
name=request.name,
algorithm_name="algorithm", # 注意:在实际实现中,应该从仓库信息中获取
version=request.version,
host=request.host,
port=request.port,
api_url=deploy_result["api_url"],
status=deploy_result["status"],
config={
"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()
}
}
finally:
db.close()
@router.get("", response_model=ServiceListResponse)
async def list_services(
current_user: dict = Depends(get_current_active_user)
):
"""获取服务列表"""
# 检查用户权限
if current_user.role != "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()
))
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: dict = Depends(get_current_active_user)
):
"""获取服务详情"""
# 检查用户权限
if current_user.role != "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()
)
)
finally:
db.close()
@router.post("/{service_id}/start")
async def start_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
):
"""启动服务"""
# 检查用户权限
if current_user.role != "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"]:
raise HTTPException(status_code=400, detail=f"服务启动失败: {start_result['error']}")
# 更新服务状态
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: dict = Depends(get_current_active_user)
):
"""停止服务"""
# 检查用户权限
if current_user.role != "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: dict = Depends(get_current_active_user)
):
"""重启服务"""
# 检查用户权限
if current_user.role != "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: dict = Depends(get_current_active_user)
):
"""删除服务"""
# 检查用户权限
if current_user.role != "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: dict = Depends(get_current_active_user)
):
"""获取服务状态"""
# 检查用户权限
if current_user.role != "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: dict = Depends(get_current_active_user)
):
"""获取服务日志"""
# 检查用户权限
if current_user.role != "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: dict = Depends(get_current_active_user)
):
"""获取仓库中的算法列表"""
# 检查用户权限
if current_user.role != "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))

129
backend/app/routes/user.py Normal file
View File

@@ -0,0 +1,129 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from typing import List
from app.models.database import get_db
from app.schemas.user import UserCreate, UserUpdate, UserResponse, UserListResponse, Token, LoginRequest
from app.services.user import UserService
# 创建路由器
router = APIRouter(prefix="/users", tags=["users"])
# OAuth2密码Bearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")
async def get_current_active_user(db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)):
"""获取当前活跃用户"""
user = UserService.get_current_user(db, token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
if user.status != "active":
raise HTTPException(status_code=400, detail="Inactive user")
return user
from app.schemas.user import LoginRequest
@router.post("/login", response_model=Token)
async def login(login_request: LoginRequest, db: Session = Depends(get_db)):
"""用户登录"""
user = UserService.authenticate_user(db, login_request.username, login_request.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 创建访问令牌
access_token = UserService.create_access_token(
data={"sub": user.username, "user_id": user.id}
)
return {"access_token": access_token, "token_type": "bearer"}
@router.post("/register", response_model=UserResponse)
async def register(user: UserCreate, db: Session = Depends(get_db)):
"""用户注册"""
# 检查用户名是否已存在
if UserService.get_user_by_username(db, user.username):
raise HTTPException(status_code=400, detail="Username already registered")
# 检查邮箱是否已存在
if UserService.get_user_by_email(db, user.email):
raise HTTPException(status_code=400, detail="Email already registered")
# 创建用户
db_user = UserService.create_user(db, user)
return db_user
@router.get("/me", response_model=UserResponse)
async def read_users_me(current_user: UserResponse = Depends(get_current_active_user)):
"""获取当前用户信息"""
return current_user
@router.get("/", response_model=UserListResponse)
async def get_users(
skip: int = 0,
limit: int = 100,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取用户列表"""
# 只有管理员可以查看用户列表
if current_user.role != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
users = UserService.get_users(db, skip=skip, limit=limit)
return {"users": users, "total": len(users)}
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
user_id: str,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取用户信息"""
# 只有管理员或用户本人可以查看用户信息
if current_user.role != "admin" and current_user.id != user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
user = UserService.get_user_by_id(db, user_id)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
@router.put("/{user_id}", response_model=UserResponse)
async def update_user(
user_id: str,
user_update: UserUpdate,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""更新用户信息"""
# 只有管理员或用户本人可以更新用户信息
if current_user.role != "admin" and current_user.id != user_id:
raise HTTPException(status_code=403, detail="Not enough permissions")
# 非管理员只能更新自己的信息,不能更新角色
if current_user.role != "admin" and "role" in user_update.dict():
raise HTTPException(status_code=403, detail="Cannot update role")
user = UserService.update_user(db, user_id, user_update)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user