#!/usr/bin/env python # -*- coding: utf-8 -*- """ 数据库管理API(异步版本) """ import logging from typing import List from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from core.database_manager.schema import ( ColumnInfo, ConstraintInfo, DatabaseConfig, DatabaseCreateIn, DatabaseInfo, DatabaseOperationOut, DatabaseRenameIn, DataOperationOut, DeleteDataIn, ExecuteDDLIn, ExecuteDDLOut, ExecuteSQLIn, ExecuteSQLOut, IndexInfo, InsertDataIn, QueryDataIn, QueryDataOut, SchemaCreateIn, SchemaInfo, SchemaOperationOut, SchemaRenameIn, TableInfo, TableStructure, UpdateDataIn, ViewInfo, ViewStructure, ) from core.database_manager.service import ( AsyncDatabaseManagerService, DatabaseManagerForbidden, DatabaseManagerValidationError, ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/database_manager", tags=["数据库管理"]) async def _get_service(db_name: str, db: AsyncSession) -> AsyncDatabaseManagerService: try: return await AsyncDatabaseManagerService.create(db_name, db) except ValueError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc def _handle_manager_error(exc: Exception) -> None: if isinstance(exc, DatabaseManagerForbidden): raise HTTPException(status_code=403, detail=str(exc)) from exc if isinstance(exc, DatabaseManagerValidationError): raise HTTPException(status_code=400, detail=str(exc)) from exc raise exc # ============ 数据库配置 ============ @router.get("/configs", response_model=List[DatabaseConfig], summary="获取数据库配置列表") async def get_database_configs(db: AsyncSession = Depends(get_db)): """获取所有配置的数据库连接信息""" return await AsyncDatabaseManagerService.get_database_configs_async(db) @router.post("/{db_name}/test", summary="测试数据库连接") async def test_database_connection(db_name: str, db: AsyncSession = Depends(get_db)): """测试指定数据库的连接""" service = await _get_service(db_name, db) return await service.test_connection() # ============ 数据库管理 ============ @router.get("/{db_name}/databases", response_model=List[DatabaseInfo], summary="获取数据库列表") async def get_databases(db_name: str, db: AsyncSession = Depends(get_db)): """获取所有数据库""" service = await _get_service(db_name, db) return await service.get_databases() @router.post("/{db_name}/databases", response_model=DatabaseOperationOut, summary="创建数据库") async def create_database( db_name: str, data: DatabaseCreateIn, db: AsyncSession = Depends(get_db), ): """创建新数据库""" try: service = await _get_service(db_name, db) success = await service.create_database( name=data.name, owner=data.owner, encoding=data.encoding, template=data.template, charset=data.charset, collation=data.collation, ) return DatabaseOperationOut( success=success, message="数据库创建成功" if success else "数据库创建失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to create database: {e}") return DatabaseOperationOut(success=False, message=str(e)) @router.patch( "/{db_name}/databases/{database_name}", response_model=DatabaseOperationOut, summary="重命名数据库", ) async def rename_database( db_name: str, database_name: str, data: DatabaseRenameIn, db: AsyncSession = Depends(get_db), ): """重命名数据库(PostgreSQL / SQL Server)""" try: service = await _get_service(db_name, db) success = await service.rename_database(database_name, data.new_name) return DatabaseOperationOut( success=success, message="数据库重命名成功" if success else "数据库重命名失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to rename database: {e}") return DatabaseOperationOut(success=False, message=str(e)) @router.delete("/{db_name}/databases/{database_name}", response_model=DatabaseOperationOut, summary="删除数据库") async def drop_database( db_name: str, database_name: str, db: AsyncSession = Depends(get_db), ): """删除数据库""" try: service = await _get_service(db_name, db) success = await service.drop_database(database_name) return DatabaseOperationOut( success=success, message="数据库删除成功" if success else "数据库删除失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to drop database: {e}") return DatabaseOperationOut(success=False, message=str(e)) # ============ Schema管理(PostgreSQL) ============ @router.get("/{db_name}/schemas", response_model=List[SchemaInfo], summary="获取Schema列表") async def get_schemas( db_name: str, database: str = None, db: AsyncSession = Depends(get_db), ): """获取所有Schema(仅PostgreSQL)""" service = await _get_service(db_name, db) return await service.get_schemas(database) @router.post("/{db_name}/schemas", response_model=SchemaOperationOut, summary="创建Schema") async def create_schema( db_name: str, data: SchemaCreateIn, db: AsyncSession = Depends(get_db), ): """创建 Schema(PostgreSQL / SQL Server)""" try: service = await _get_service(db_name, db) success = await service.create_schema(data.name, data.database, data.owner) return SchemaOperationOut( success=success, message="Schema 创建成功" if success else "Schema 创建失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to create schema: {e}") return SchemaOperationOut(success=False, message=str(e)) @router.delete( "/{db_name}/schemas/{schema_name}", response_model=SchemaOperationOut, summary="删除Schema", ) async def drop_schema( db_name: str, schema_name: str, database: str = Query(None, description="数据库名"), cascade: bool = Query(True, description="是否 CASCADE"), db: AsyncSession = Depends(get_db), ): """删除 Schema(PostgreSQL / SQL Server)""" try: service = await _get_service(db_name, db) success = await service.drop_schema(schema_name, database, cascade) return SchemaOperationOut( success=success, message="Schema 删除成功" if success else "Schema 删除失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to drop schema: {e}") return SchemaOperationOut(success=False, message=str(e)) @router.patch( "/{db_name}/schemas/{schema_name}", response_model=SchemaOperationOut, summary="重命名Schema", ) async def rename_schema( db_name: str, schema_name: str, data: SchemaRenameIn, db: AsyncSession = Depends(get_db), ): """重命名 Schema(仅 PostgreSQL)""" try: service = await _get_service(db_name, db) success = await service.rename_schema(schema_name, data.new_name, data.database) return SchemaOperationOut( success=success, message="Schema 重命名成功" if success else "Schema 重命名失败", ) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e) except Exception as e: logger.error(f"Failed to rename schema: {e}") return SchemaOperationOut(success=False, message=str(e)) # ============ 表管理 ============ @router.get("/{db_name}/tables", response_model=List[TableInfo], summary="获取表列表") async def get_tables( db_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取指定schema/database的所有表""" service = await _get_service(db_name, db) return await service.get_tables(database=database, schema_name=schema_name) @router.get("/{db_name}/tables/{table_name}/structure", response_model=TableStructure, summary="获取表结构") async def get_table_structure( db_name: str, table_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取表的详细结构""" service = await _get_service(db_name, db) try: return await service.get_table_structure( table_name, database=database, schema_name=schema_name ) except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) from e @router.get("/{db_name}/tables/{table_name}/ddl", summary="获取表DDL") async def get_table_ddl( db_name: str, table_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取表的DDL语句""" service = await _get_service(db_name, db) ddl = await service.get_table_ddl(table_name, schema_name, database) return {"ddl": ddl} @router.get("/{db_name}/tables/{table_name}/columns", response_model=List[ColumnInfo], summary="获取表字段") async def get_table_columns( db_name: str, table_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取表的字段信息""" service = await _get_service(db_name, db) return await service.get_table_columns(table_name, schema_name, database) @router.get("/{db_name}/tables/{table_name}/indexes", response_model=List[IndexInfo], summary="获取表索引") async def get_table_indexes( db_name: str, table_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取表的索引信息""" service = await _get_service(db_name, db) return await service.get_table_indexes(table_name, schema_name, database) @router.get("/{db_name}/tables/{table_name}/constraints", response_model=List[ConstraintInfo], summary="获取表约束") async def get_table_constraints( db_name: str, table_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取表的约束信息""" service = await _get_service(db_name, db) return await service.get_table_constraints(table_name, schema_name, database) # ============ 视图管理 ============ @router.get("/{db_name}/views", response_model=List[ViewInfo], summary="获取视图列表") async def get_views( db_name: str, database: str = Query(None, description="数据库名"), schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取指定schema/database的所有视图""" service = await _get_service(db_name, db) return await service.get_views(database=database, schema_name=schema_name) @router.get("/{db_name}/views/{view_name}/structure", response_model=ViewStructure, summary="获取视图结构") async def get_view_structure( db_name: str, view_name: str, schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取视图的详细结构""" service = await _get_service(db_name, db) try: return await service.get_view_structure(view_name, schema_name) except ValueError as e: raise HTTPException(status_code=404, detail=str(e)) from e @router.get("/{db_name}/views/{view_name}/definition", summary="获取视图定义") async def get_view_definition( db_name: str, view_name: str, schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取视图的定义SQL""" service = await _get_service(db_name, db) definition = await service.get_view_definition(view_name, schema_name) return {"definition": definition} @router.get("/{db_name}/views/{view_name}/dependencies", response_model=List[str], summary="获取视图依赖") async def get_view_dependencies( db_name: str, view_name: str, schema_name: str = Query(None, description="Schema名"), db: AsyncSession = Depends(get_db), ): """获取视图依赖的表列表""" service = await _get_service(db_name, db) return await service.get_view_dependencies(view_name, schema_name) # ============ 数据查询 ============ @router.post("/{db_name}/query", response_model=QueryDataOut, summary="查询表数据") async def query_data( db_name: str, data: QueryDataIn, db: AsyncSession = Depends(get_db), ): """分页查询表数据""" service = await _get_service(db_name, db) return await service.query_data( table_name=data.table_name, schema_name=data.schema_name, database=data.database, page=data.page, page_size=data.page_size, where=data.where, order_by=data.order_by, ) # ============ SQL执行 ============ @router.post("/{db_name}/execute", response_model=ExecuteSQLOut, summary="执行SQL") async def execute_sql( db_name: str, data: ExecuteSQLIn, db: AsyncSession = Depends(get_db), ): """执行自定义SQL语句""" service = await _get_service(db_name, db) return await service.execute_sql(data.sql, data.is_query) # ============ 数据操作 ============ @router.post("/{db_name}/data/insert", response_model=DataOperationOut, summary="插入数据") async def insert_data( db_name: str, data: InsertDataIn, db: AsyncSession = Depends(get_db), ): """向表中插入数据""" service = await _get_service(db_name, db) return await service.insert_data(data.table_name, data.data, data.schema_name) @router.post("/{db_name}/data/update", response_model=DataOperationOut, summary="更新数据") async def update_data( db_name: str, data: UpdateDataIn, db: AsyncSession = Depends(get_db), ): """更新表中的数据""" service = await _get_service(db_name, db) return await service.update_data( data.table_name, data.data, data.where, data.schema_name ) @router.post("/{db_name}/data/delete", response_model=DataOperationOut, summary="删除数据") async def delete_data( db_name: str, data: DeleteDataIn, db: AsyncSession = Depends(get_db), ): """删除表中的数据""" service = await _get_service(db_name, db) return await service.delete_data(data.table_name, data.where, data.schema_name) # ============ DDL操作 ============ @router.post("/{db_name}/execute/ddl", response_model=ExecuteDDLOut, summary="执行DDL语句") async def execute_ddl( db_name: str, data: ExecuteDDLIn, db: AsyncSession = Depends(get_db), ): """执行DDL语句(CREATE TABLE, ALTER TABLE, DROP TABLE等)""" try: service = await _get_service(db_name, db) result = await service.execute_ddl(data.sql, data.database, data.schema_name) return ExecuteDDLOut(**result) except HTTPException: raise except (DatabaseManagerForbidden, DatabaseManagerValidationError) as e: _handle_manager_error(e)