from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.core.security import get_current_user, require_role from app.models.user import User from app.models.instance import Instance from app.schemas.instance import InstanceCreate, InstanceUpdate, InstanceResponse, ConnectionTestResult from app.services.instance_service import InstanceService from app.core.crypto import encrypt_password, decrypt_password from app.services.audit_log_service import AuditLogService router = APIRouter(prefix="/instances", tags=["数据源管理"]) @router.get("", response_model=List[InstanceResponse]) async def list_instances( db_type: Optional[str] = None, env: Optional[str] = None, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): stmt = select(Instance).order_by(Instance.id) if db_type: stmt = stmt.where(Instance.db_type == db_type) if env: stmt = stmt.where(Instance.env == env) result = await db.execute(stmt) return result.scalars().all() @router.post("", response_model=InstanceResponse, status_code=201) async def create_instance( data: InstanceCreate, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin", "dba")), ): instance = Instance( name=data.name, db_type=data.db_type, host=data.host, port=data.port, username=data.username, password=encrypt_password(data.password), database=data.database, env=data.env, description=data.description, ) db.add(instance) await db.flush() await AuditLogService.log( db, user_id=current_user.id, action="CREATE_INSTANCE", target_type="instance", target_id=instance.id, detail=f"创建数据源: {data.name} ({data.db_type}@{data.host}:{data.port})", ) await db.commit() await db.refresh(instance) return instance @router.get("/{instance_id}", response_model=InstanceResponse) async def get_instance( instance_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") return instance @router.put("/{instance_id}", response_model=InstanceResponse) async def update_instance( instance_id: int, data: InstanceUpdate, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin", "dba")), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") update_data = data.model_dump(exclude_unset=True) if "password" in update_data: update_data["password"] = encrypt_password(update_data["password"]) for key, value in update_data.items(): setattr(instance, key, value) await db.commit() await db.refresh(instance) return instance @router.delete("/{instance_id}") async def delete_instance( instance_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin", "dba")), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") await AuditLogService.log( db, user_id=current_user.id, action="DELETE_INSTANCE", target_type="instance", target_id=instance.id, detail=f"删除数据源: {instance.name}", ) await db.delete(instance) await db.commit() return {"message": "数据源已删除"} @router.post("/{instance_id}/test", response_model=ConnectionTestResult) async def test_connection( instance_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(require_role("admin", "dba")), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") # Decrypt password for connection test instance.password = decrypt_password(instance.password) return await InstanceService.test_connection(instance) @router.get("/{instance_id}/databases") async def list_databases( instance_id: int, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") return await InstanceService.list_databases(instance) @router.get("/{instance_id}/databases/{database}/tables") async def list_tables( instance_id: int, database: str, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): result = await db.execute(select(Instance).where(Instance.id == instance_id)) instance = result.scalar_one_or_none() if not instance: raise HTTPException(status_code=404, detail="数据源不存在") return await InstanceService.list_tables(instance, database)