""" 管理 API 处理器 提供项目映射和 API 密钥管理功能 """ import secrets from datetime import datetime, timedelta from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel from sqlalchemy.orm import Session from app.database import get_db from app.models.api_key import APIKey from app.models.project_mapping import ProjectMapping from app.auth import get_current_user router = APIRouter(prefix="/api/admin", tags=["admin"]) # API 密钥相关模型 class APIKeyResponse(BaseModel): id: int name: str key_prefix: str created_at: datetime last_used: datetime is_active: bool class Config: from_attributes = True class CreateAPIKeyRequest(BaseModel): name: str class CreateAPIKeyResponse(BaseModel): id: int name: str key: str created_at: datetime # 项目映射相关模型 class ProjectMappingRequest(BaseModel): repository_name: str default_job: str branch_jobs: Optional[List[dict]] = [] branch_patterns: Optional[List[dict]] = [] class ProjectMappingResponse(BaseModel): id: int repository_name: str default_job: str branch_jobs: List[dict] branch_patterns: List[dict] created_at: datetime updated_at: datetime class Config: from_attributes = True # API 密钥管理端点 @router.get("/api-keys", response_model=List[APIKeyResponse]) async def list_api_keys( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """列出所有 API 密钥""" try: api_keys = db.query(APIKey).order_by(APIKey.created_at.desc()).all() return api_keys except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to list API keys: {str(e)}") @router.post("/api-keys", response_model=CreateAPIKeyResponse) async def create_api_key( request: CreateAPIKeyRequest, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """创建新的 API 密钥""" try: # 生成 API 密钥 api_key = secrets.token_urlsafe(32) key_prefix = api_key[:8] # 显示前8位作为前缀 # 创建数据库记录 db_api_key = APIKey( name=request.name, key_hash=api_key, # 实际应用中应该哈希存储 key_prefix=key_prefix, created_at=datetime.utcnow(), last_used=datetime.utcnow(), is_active=True ) db.add(db_api_key) db.commit() db.refresh(db_api_key) return CreateAPIKeyResponse( id=db_api_key.id, name=db_api_key.name, key=api_key, # 只在创建时返回完整密钥 created_at=db_api_key.created_at ) except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to create API key: {str(e)}") @router.delete("/api-keys/{key_id}") async def delete_api_key( key_id: int, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """删除 API 密钥""" try: api_key = db.query(APIKey).filter(APIKey.id == key_id).first() if not api_key: raise HTTPException(status_code=404, detail="API key not found") db.delete(api_key) db.commit() return {"message": "API key deleted successfully"} except HTTPException: raise except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to delete API key: {str(e)}") @router.post("/api-keys/{key_id}/revoke") async def revoke_api_key( key_id: int, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """撤销 API 密钥""" try: api_key = db.query(APIKey).filter(APIKey.id == key_id).first() if not api_key: raise HTTPException(status_code=404, detail="API key not found") api_key.is_active = False db.commit() return {"message": "API key revoked successfully"} except HTTPException: raise except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to revoke API key: {str(e)}") # 项目映射管理端点 @router.get("/projects", response_model=List[ProjectMappingResponse]) async def list_project_mappings( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """列出所有项目映射""" try: mappings = db.query(ProjectMapping).order_by(ProjectMapping.created_at.desc()).all() return mappings except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to list project mappings: {str(e)}") @router.post("/projects", response_model=ProjectMappingResponse) async def create_project_mapping( request: ProjectMappingRequest, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """创建项目映射""" try: # 检查是否已存在 existing = db.query(ProjectMapping).filter( ProjectMapping.repository_name == request.repository_name ).first() if existing: raise HTTPException(status_code=400, detail="Project mapping already exists") # 创建新映射 mapping = ProjectMapping( repository_name=request.repository_name, default_job=request.default_job, branch_jobs=request.branch_jobs or [], branch_patterns=request.branch_patterns or [], created_at=datetime.utcnow(), updated_at=datetime.utcnow() ) db.add(mapping) db.commit() db.refresh(mapping) return mapping except HTTPException: raise except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to create project mapping: {str(e)}") @router.get("/projects/{repository_name}", response_model=ProjectMappingResponse) async def get_project_mapping( repository_name: str, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """获取项目映射""" try: mapping = db.query(ProjectMapping).filter( ProjectMapping.repository_name == repository_name ).first() if not mapping: raise HTTPException(status_code=404, detail="Project mapping not found") return mapping except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get project mapping: {str(e)}") @router.delete("/projects/{repository_name}") async def delete_project_mapping( repository_name: str, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """删除项目映射""" try: mapping = db.query(ProjectMapping).filter( ProjectMapping.repository_name == repository_name ).first() if not mapping: raise HTTPException(status_code=404, detail="Project mapping not found") db.delete(mapping) db.commit() return {"message": "Project mapping deleted successfully"} except HTTPException: raise except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"Failed to delete project mapping: {str(e)}") # 统计信息端点 @router.get("/stats") async def get_admin_stats( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): """获取管理统计信息""" try: # API 密钥统计 total_keys = db.query(APIKey).count() active_keys = db.query(APIKey).filter(APIKey.is_active == True).count() # 最近使用的密钥 recent_keys = db.query(APIKey).filter( APIKey.last_used >= datetime.utcnow() - timedelta(days=7) ).count() # 项目映射统计 total_mappings = db.query(ProjectMapping).count() return { "api_keys": { "total": total_keys, "active": active_keys, "recently_used": recent_keys }, "project_mappings": { "total": total_mappings } } except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get admin stats: {str(e)}")