仓库模块完了

This commit is contained in:
2026-02-08 20:06:35 +08:00
parent 20e1deae21
commit f145df4fa6
29 changed files with 1415 additions and 993 deletions

View File

@@ -4,7 +4,7 @@ from fastapi.middleware.trustedhost import TrustedHostMiddleware
from app.config.settings import settings
from app.models.database import engine, Base
from app.routes import user, api_key, algorithm, openai, gateway, services, data_management, monitoring, permissions, history, deployment, gitea, repositories
from app.routes import user, algorithm, openai, gateway, services, data_management, monitoring, permissions, history, deployment, gitea, repositories
# 创建数据库表
Base.metadata.create_all(bind=engine)
@@ -37,7 +37,6 @@ app.add_middleware(
# 注册路由
app.include_router(user.router, prefix=settings.API_V1_STR)
app.include_router(api_key.router, prefix=settings.API_V1_STR)
app.include_router(algorithm.router, prefix=settings.API_V1_STR)
app.include_router(openai.router, prefix=settings.API_V1_STR)
app.include_router(gateway.router, prefix=settings.API_V1_STR)

View File

@@ -46,6 +46,20 @@ class AlgorithmVersion(Base):
calls = relationship("AlgorithmCall", back_populates="version")
class Role(Base):
"""角色模型"""
__tablename__ = "roles"
id = Column(String, primary_key=True, index=True)
name = Column(String, unique=True, nullable=False, index=True) # admin, user
description = Column(Text, default="")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
users = relationship("User", back_populates="role")
class User(Base):
"""用户模型"""
__tablename__ = "users"
@@ -54,31 +68,17 @@ class User(Base):
username = Column(String, unique=True, nullable=False, index=True)
email = Column(String, unique=True, nullable=False, index=True)
password_hash = Column(String, nullable=False)
role = Column(String, default="user", index=True) # admin, user, customer
role_id = Column(String, ForeignKey("roles.id"), nullable=False, index=True)
status = Column(String, default="active", index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
api_keys = relationship("APIKey", back_populates="user", cascade="all, delete-orphan")
calls = relationship("AlgorithmCall", back_populates="user")
role = relationship("Role", back_populates="users")
class APIKey(Base):
"""API密钥模型"""
__tablename__ = "api_keys"
id = Column(String, primary_key=True, index=True)
user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True)
key = Column(String, unique=True, nullable=False, index=True)
name = Column(String, nullable=False)
expires_at = Column(DateTime(timezone=True), nullable=False)
status = Column(String, default="active", index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
user = relationship("User", back_populates="api_keys")
class AlgorithmCall(Base):
@@ -138,12 +138,28 @@ class AlgorithmRepository(Base):
algorithm = relationship("Algorithm", back_populates="repository", uselist=False)
class ServiceGroup(Base):
"""服务分组模型"""
__tablename__ = "service_groups"
id = Column(String, primary_key=True, index=True)
name = Column(String, nullable=False, unique=True, index=True) # 分组名称
description = Column(Text, default="") # 分组描述
status = Column(String, default="active", index=True) # 状态
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
services = relationship("AlgorithmService", back_populates="group")
class AlgorithmService(Base):
"""算法服务模型"""
__tablename__ = "algorithm_services"
id = Column(String, primary_key=True, index=True)
service_id = Column(String, unique=True, nullable=False, index=True) # 服务ID
group_id = Column(String, ForeignKey("service_groups.id"), nullable=True, index=True) # 分组ID
name = Column(String, nullable=False, index=True) # 服务名称
algorithm_name = Column(String, nullable=False) # 算法名称
version = Column(String, nullable=False) # 版本
@@ -156,6 +172,9 @@ class AlgorithmService(Base):
last_heartbeat = Column(DateTime(timezone=True), nullable=True) # 最后心跳时间
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# 关系
group = relationship("ServiceGroup", back_populates="services")
# 添加Algorithm模型的repository关系

View File

@@ -1,12 +1,11 @@
from fastapi import APIRouter
from app.routes import user, algorithm, api_key, history, gateway, monitoring, openai, deployment
from app.routes import user, algorithm, 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"])

View File

@@ -1,88 +0,0 @@
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

@@ -243,22 +243,3 @@ async def get_user_role_based_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

@@ -8,6 +8,7 @@ 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.schemas.user import UserResponse
from app.gitea.service import gitea_service
router = APIRouter(prefix="/repositories", tags=["repositories"])
@@ -38,11 +39,11 @@ class UpdateRepositoryRequest(BaseModel):
@router.post("", status_code=status.HTTP_201_CREATED)
async def create_repository(
request: CreateRepositoryRequest,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""创建算法仓库"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -92,11 +93,11 @@ async def create_repository(
@router.get("")
async def list_repositories(
algorithm_id: Optional[str] = None,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取算法仓库列表"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -139,11 +140,11 @@ async def list_repositories(
@router.get("/{repo_id}")
async def get_repository(
repo_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取单个算法仓库"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -179,11 +180,11 @@ async def get_repository(
async def update_repository(
repo_id: str,
request: UpdateRepositoryRequest,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""更新算法仓库"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -239,11 +240,11 @@ async def update_repository(
@router.delete("/{repo_id}")
async def delete_repository(
repo_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""删除算法仓库"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话

View File

@@ -6,9 +6,10 @@ from pydantic import BaseModel
import uuid
import os
from app.models.models import AlgorithmService
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
@@ -83,6 +84,46 @@ class RepositoryAlgorithmsResponse(BaseModel):
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()
@@ -92,19 +133,23 @@ 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)
current_user: UserResponse = Depends(get_current_active_user)
):
"""注册新服务"""
# 检查用户权限
if current_user.role != "admin":
if 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. 分析项目
repo_path = f"/tmp/repository_{request.repository_id}"
@@ -157,7 +202,7 @@ def main(data):
id=str(uuid.uuid4()),
service_id=service_id,
name=request.name,
algorithm_name="algorithm", # 注意:在实际实现中,应该从仓库信息中获取
algorithm_name=repo.name, # 使用仓库名称作为算法名称
version=request.version,
host=request.host,
port=request.port,
@@ -200,11 +245,11 @@ def main(data):
@router.get("", response_model=ServiceListResponse)
async def list_services(
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取服务列表"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -241,11 +286,11 @@ async def list_services(
@router.get("/{service_id}", response_model=ServiceDetailResponse)
async def get_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取服务详情"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -281,11 +326,11 @@ async def get_service(
@router.post("/{service_id}/start")
async def start_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""启动服务"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -325,11 +370,11 @@ async def start_service(
@router.post("/{service_id}/stop")
async def stop_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""停止服务"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -369,11 +414,11 @@ async def stop_service(
@router.post("/{service_id}/restart")
async def restart_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""重启服务"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -413,11 +458,11 @@ async def restart_service(
@router.delete("/{service_id}")
async def delete_service(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""删除服务"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -456,11 +501,11 @@ async def delete_service(
@router.get("/{service_id}/status")
async def get_service_status(
service_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取服务状态"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -496,11 +541,11 @@ async def get_service_status(
async def get_service_logs(
service_id: str,
lines: int = 100,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取服务日志"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
# 创建数据库会话
@@ -534,11 +579,11 @@ async def get_service_logs(
@router.get("/repository/algorithms")
async def get_repository_algorithms(
repository_id: str,
current_user: dict = Depends(get_current_active_user)
current_user: UserResponse = Depends(get_current_active_user)
):
"""获取仓库中的算法列表"""
# 检查用户权限
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Insufficient permissions")
try:
@@ -567,3 +612,453 @@ async def get_repository_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()

View File

@@ -2,9 +2,12 @@ from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from typing import List
from jose import JWTError, jwt
from app.config.settings import settings
from app.models.database import get_db
from app.schemas.user import UserCreate, UserUpdate, UserResponse, UserListResponse, Token, LoginRequest
from app.models.models import User, Role
from app.schemas.user import UserCreate, UserUpdate, UserResponse, UserListResponse, Token, LoginRequest, RoleCreate, RoleResponse
from app.services.user import UserService
# 创建路由器
@@ -16,16 +19,72 @@ 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:
try:
# 检查令牌是否在黑名单中
if UserService.is_token_blacklisted(token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
# 解码令牌
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
# 使用UserService获取用户信息避免直接使用User模型
user = UserService.get_user_by_username(db, username)
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")
# 使用UserService获取角色信息
role = UserService.get_role_by_id(db, user.role_id)
# 构建角色响应
role_response = None
if role:
role_response = RoleResponse(
id=role.id,
name=role.name,
description=role.description,
created_at=role.created_at,
updated_at=role.updated_at
)
# 构建用户响应
user_response = UserResponse(
id=user.id,
username=user.username,
email=user.email,
role_id=user.role_id,
status=user.status,
created_at=user.created_at,
updated_at=user.updated_at,
role=role_response,
role_name=role.name if role else None
)
return user_response
except JWTError:
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
@@ -60,6 +119,10 @@ async def register(user: UserCreate, db: Session = Depends(get_db)):
if UserService.get_user_by_email(db, user.email):
raise HTTPException(status_code=400, detail="Email already registered")
# 检查角色是否存在
if not UserService.get_role_by_id(db, user.role_id):
raise HTTPException(status_code=400, detail="Role not found")
# 创建用户
db_user = UserService.create_user(db, user)
@@ -81,7 +144,7 @@ async def get_users(
):
"""获取用户列表"""
# 只有管理员可以查看用户列表
if current_user.role != "admin":
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
users = UserService.get_users(db, skip=skip, limit=limit)
@@ -96,7 +159,7 @@ async def get_user(
):
"""获取用户信息"""
# 只有管理员或用户本人可以查看用户信息
if current_user.role != "admin" and current_user.id != user_id:
if current_user.role_name != "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)
@@ -115,15 +178,98 @@ async def update_user(
):
"""更新用户信息"""
# 只有管理员或用户本人可以更新用户信息
if current_user.role != "admin" and current_user.id != user_id:
if current_user.role_name != "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():
if current_user.role_name != "admin" and "role_id" in user_update.dict():
raise HTTPException(status_code=403, detail="Cannot update role")
# 检查角色是否存在
if "role_id" in user_update.dict():
if not UserService.get_role_by_id(db, user_update.role_id):
raise HTTPException(status_code=400, detail="Role not found")
user = UserService.update_user(db, user_id, user_update)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
@router.delete("/{user_id}")
async def delete_user(
user_id: str,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""删除用户"""
# 只有管理员可以删除用户
if current_user.role_name != "admin":
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")
# 删除用户
db.delete(user)
db.commit()
return {"message": "User deleted successfully"}
# 角色管理API
@router.post("/roles", response_model=RoleResponse)
async def create_role(
role: RoleCreate,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""创建角色"""
# 只有管理员可以创建角色
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
# 检查角色名称是否已存在
if UserService.get_role_by_name(db, role.name):
raise HTTPException(status_code=400, detail="Role name already exists")
# 创建角色
db_role = UserService.create_role(db, role)
return db_role
@router.get("/roles", response_model=List[RoleResponse])
async def get_roles(
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取角色列表"""
# 只有管理员可以查看所有角色
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
roles = UserService.get_roles(db)
return roles
@router.get("/roles/{role_id}", response_model=RoleResponse)
async def get_role(
role_id: str,
current_user: UserResponse = Depends(get_current_active_user),
db: Session = Depends(get_db)
):
"""获取角色详情"""
# 只有管理员可以查看角色详情
if current_user.role_name != "admin":
raise HTTPException(status_code=403, detail="Not enough permissions")
role = UserService.get_role_by_id(db, role_id)
if not role:
raise HTTPException(status_code=404, detail="Role not found")
return role

View File

@@ -3,12 +3,30 @@ from typing import Optional, List
from datetime import datetime
class RoleBase(BaseModel):
"""角色基础模式"""
id: Optional[str] = None
name: str = Field(..., description="角色名称")
description: str = Field(default="", description="角色描述")
class RoleCreate(RoleBase):
"""创建角色模式"""
pass
class RoleResponse(RoleBase):
"""角色响应模式"""
created_at: datetime
updated_at: Optional[datetime] = None
class UserBase(BaseModel):
"""用户基础模式"""
id: Optional[str] = None
username: str = Field(..., description="用户名")
email: EmailStr = Field(..., description="邮箱")
role: str = Field(default="user", description="用户角色")
role_id: str = Field(..., description="角色ID")
class UserCreate(UserBase):
@@ -20,7 +38,7 @@ class UserUpdate(BaseModel):
"""更新用户模式"""
username: Optional[str] = None
email: Optional[EmailStr] = None
role: Optional[str] = None
role_id: Optional[str] = None
status: Optional[str] = None
password: Optional[str] = None
@@ -30,6 +48,8 @@ class UserResponse(UserBase):
status: str
created_at: datetime
updated_at: Optional[datetime] = None
role: Optional[RoleResponse] = None
role_name: Optional[str] = None
class UserListResponse(BaseModel):
@@ -56,28 +76,4 @@ class LoginRequest(BaseModel):
password: str = Field(..., description="密码")
class APIKeyBase(BaseModel):
"""API密钥基础模式"""
id: Optional[str] = None
user_id: str = Field(..., description="用户ID")
name: str = Field(..., description="密钥名称")
class APIKeyCreate(APIKeyBase):
"""创建API密钥模式"""
expires_at: datetime = Field(..., description="过期时间")
class APIKeyResponse(APIKeyBase):
"""API密钥响应模式"""
key: str
expires_at: datetime
status: str
created_at: datetime
updated_at: Optional[datetime] = None
class APIKeyListResponse(BaseModel):
"""API密钥列表响应模式"""
api_keys: List[APIKeyResponse]
total: int

View File

@@ -8,7 +8,7 @@ import logging
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_
from app.models.models import User, Algorithm, APIKey
from app.models.models import User, Algorithm
logger = logging.getLogger(__name__)
@@ -209,26 +209,7 @@ class PermissionManager:
logger.error(f"Error getting algorithm permissions: {str(e)}")
return []
def check_api_key_access(self, db: Session, api_key_value: str, algorithm_id: str) -> bool:
"""检查API密钥对算法的访问权限"""
try:
# 通过API密钥查找用户
api_key = db.query(APIKey).filter(
APIKey.key == api_key_value,
APIKey.status == "active"
).first()
if not api_key:
return False
# 检查用户对算法的访问权限
return self.check_algorithm_access(
db, api_key.user_id, algorithm_id, PermissionType.EXECUTE
)
except Exception as e:
logger.error(f"Error checking API key access: {str(e)}")
return False
def validate_user_algorithm_operation(self, db: Session, user_id: str, algorithm_id: str,
operation: str) -> bool:

View File

@@ -2,12 +2,12 @@ from datetime import datetime, timedelta
from typing import Optional, List
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session, joinedload
import uuid
from app.config.settings import settings
from app.models.models import User, APIKey
from app.schemas.user import UserCreate, UserUpdate, TokenData, APIKeyCreate
from app.models.models import User, Role
from app.schemas.user import UserCreate, UserUpdate, TokenData, RoleCreate
from app.utils.cache import cache
# 密码加密上下文使用pbkdf2_sha256方案避免bcrypt的密码长度限制
@@ -86,12 +86,12 @@ class UserService:
@staticmethod
def get_user_by_username(db: Session, username: str) -> Optional[User]:
"""通过用户名获取用户"""
return db.query(User).filter(User.username == username).first()
return db.query(User).options(joinedload(User.role)).filter(User.username == username).first()
@staticmethod
def get_user_by_id(db: Session, user_id: str) -> Optional[User]:
"""通过ID获取用户"""
return db.query(User).filter(User.id == user_id).first()
return db.query(User).options(joinedload(User.role)).filter(User.id == user_id).first()
@staticmethod
def get_user_by_email(db: Session, email: str) -> Optional[User]:
@@ -101,7 +101,7 @@ class UserService:
@staticmethod
def get_users(db: Session, skip: int = 0, limit: int = 100) -> List[User]:
"""获取用户列表"""
return db.query(User).offset(skip).limit(limit).all()
return db.query(User).options(joinedload(User.role)).offset(skip).limit(limit).all()
@staticmethod
def create_user(db: Session, user: UserCreate) -> User:
@@ -115,7 +115,7 @@ class UserService:
username=user.username,
email=user.email,
password_hash=UserService.get_password_hash(user.password),
role=user.role
role_id=user.role_id
)
# 保存到数据库
@@ -159,80 +159,64 @@ class UserService:
if not UserService.verify_password(password, user.password_hash):
return None
return user
class APIKeyService:
"""API密钥服务类"""
@staticmethod
def create_api_key(db: Session, api_key_create: APIKeyCreate) -> APIKey:
"""创建API密钥"""
# 生成唯一ID和密钥
api_key_id = f"key-{uuid.uuid4().hex[:8]}"
api_key_value = f"sk_{uuid.uuid4().hex}"
def create_role(db: Session, role: RoleCreate) -> Role:
"""创建角色"""
# 生成唯一ID
role_id = f"role-{uuid.uuid4().hex[:8]}"
# 创建API密钥实例
db_api_key = APIKey(
id=api_key_id,
user_id=api_key_create.user_id,
name=api_key_create.name,
key=api_key_value,
expires_at=api_key_create.expires_at
# 创建角色实例
db_role = Role(
id=role_id,
name=role.name,
description=role.description
)
# 保存到数据库
db.add(db_api_key)
db.add(db_role)
db.commit()
db.refresh(db_api_key)
db.refresh(db_role)
return db_api_key
return db_role
@staticmethod
def get_api_key_by_id(db: Session, api_key_id: str) -> Optional[APIKey]:
"""通过ID获取API密钥"""
return db.query(APIKey).filter(APIKey.id == api_key_id).first()
def get_role_by_id(db: Session, role_id: str) -> Optional[Role]:
"""通过ID获取角色"""
return db.query(Role).filter(Role.id == role_id).first()
@staticmethod
def get_api_key_by_value(db: Session, api_key_value: str) -> Optional[APIKey]:
"""通过值获取API密钥"""
return db.query(APIKey).filter(APIKey.key == api_key_value).first()
def get_role_by_name(db: Session, role_name: str) -> Optional[Role]:
"""通过名称获取角色"""
return db.query(Role).filter(Role.name == role_name).first()
@staticmethod
def get_api_keys_by_user_id(db: Session, user_id: str) -> List[APIKey]:
"""通过用户ID获取API密钥列表"""
return db.query(APIKey).filter(APIKey.user_id == user_id).all()
def get_roles(db: Session, skip: int = 0, limit: int = 100) -> List[Role]:
"""获取角色列表"""
return db.query(Role).offset(skip).limit(limit).all()
@staticmethod
def revoke_api_key(db: Session, api_key_id: str) -> Optional[APIKey]:
"""撤销API密钥"""
# 获取API密钥
db_api_key = APIKeyService.get_api_key_by_id(db, api_key_id)
if not db_api_key:
return None
def init_default_roles(db: Session) -> None:
"""初始化默认角色"""
# 检查是否已存在默认角色
admin_role = UserService.get_role_by_name(db, "admin")
user_role = UserService.get_role_by_name(db, "user")
# 更新状态为已撤销
db_api_key.status = "revoked"
# 创建管理员角色
if not admin_role:
admin_role = RoleCreate(
name="admin",
description="系统管理员,拥有所有权限"
)
UserService.create_role(db, admin_role)
# 保存到数据库
db.commit()
db.refresh(db_api_key)
return db_api_key
@staticmethod
def validate_api_key(db: Session, api_key_value: str) -> Optional[APIKey]:
"""验证API密钥"""
# 获取API密钥
api_key = APIKeyService.get_api_key_by_value(db, api_key_value)
if not api_key:
return None
# 检查状态
if api_key.status != "active":
return None
# 检查是否过期
if api_key.expires_at < datetime.utcnow():
return None
return api_key
# 创建普通用户角色
if not user_role:
user_role = RoleCreate(
name="user",
description="普通用户,拥有基本权限"
)
UserService.create_role(db, user_role)