263 lines
7.5 KiB
Python
263 lines
7.5 KiB
Python
|
|
"""
|
|||
|
|
数据源管理API
|
|||
|
|
"""
|
|||
|
|
from fastapi import APIRouter, Depends, Query
|
|||
|
|
from sqlalchemy.orm import Session
|
|||
|
|
from pydantic import BaseModel
|
|||
|
|
from typing import List, Optional, Dict, Any
|
|||
|
|
from datetime import datetime
|
|||
|
|
import logging
|
|||
|
|
from app.core.database import get_db
|
|||
|
|
from app.models.data_source import DataSource
|
|||
|
|
from app.api.auth import get_current_user
|
|||
|
|
from app.models.user import User
|
|||
|
|
from app.core.exceptions import NotFoundError, ValidationError
|
|||
|
|
from app.services.data_source_connector import DataSourceConnector
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
router = APIRouter(prefix="/api/v1/data-sources", tags=["data-sources"])
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DataSourceCreate(BaseModel):
|
|||
|
|
"""数据源创建模型"""
|
|||
|
|
name: str
|
|||
|
|
type: str
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
config: Dict[str, Any]
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DataSourceUpdate(BaseModel):
|
|||
|
|
"""数据源更新模型"""
|
|||
|
|
name: Optional[str] = None
|
|||
|
|
description: Optional[str] = None
|
|||
|
|
config: Optional[Dict[str, Any]] = None
|
|||
|
|
status: Optional[str] = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DataSourceResponse(BaseModel):
|
|||
|
|
"""数据源响应模型"""
|
|||
|
|
id: str
|
|||
|
|
name: str
|
|||
|
|
type: str
|
|||
|
|
description: Optional[str]
|
|||
|
|
config: Dict[str, Any]
|
|||
|
|
status: str
|
|||
|
|
user_id: str
|
|||
|
|
last_connected_at: Optional[datetime]
|
|||
|
|
created_at: datetime
|
|||
|
|
updated_at: datetime
|
|||
|
|
|
|||
|
|
class Config:
|
|||
|
|
from_attributes = True
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.get("", response_model=List[DataSourceResponse])
|
|||
|
|
async def get_data_sources(
|
|||
|
|
skip: int = 0,
|
|||
|
|
limit: int = 100,
|
|||
|
|
type: Optional[str] = None,
|
|||
|
|
status: Optional[str] = None,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""获取数据源列表"""
|
|||
|
|
query = db.query(DataSource).filter(
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if type:
|
|||
|
|
query = query.filter(DataSource.type == type)
|
|||
|
|
|
|||
|
|
if status:
|
|||
|
|
query = query.filter(DataSource.status == status)
|
|||
|
|
|
|||
|
|
data_sources = query.order_by(DataSource.created_at.desc()).offset(skip).limit(limit).all()
|
|||
|
|
return data_sources
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("", response_model=DataSourceResponse, status_code=201)
|
|||
|
|
async def create_data_source(
|
|||
|
|
data_source_data: DataSourceCreate,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""创建数据源"""
|
|||
|
|
# 验证数据源类型
|
|||
|
|
valid_types = ['mysql', 'postgresql', 'mongodb', 'redis', 'csv', 'json', 'api', 's3']
|
|||
|
|
if data_source_data.type not in valid_types:
|
|||
|
|
raise ValidationError(f"不支持的数据源类型: {data_source_data.type}")
|
|||
|
|
|
|||
|
|
# 测试连接(可选,如果配置了连接信息)
|
|||
|
|
if data_source_data.type in ['mysql', 'postgresql', 'mongodb', 'redis']:
|
|||
|
|
try:
|
|||
|
|
connector = DataSourceConnector(data_source_data.type, data_source_data.config)
|
|||
|
|
connector.test_connection()
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.warning(f"数据源连接测试失败: {str(e)}")
|
|||
|
|
# 不阻止创建,但标记状态为error
|
|||
|
|
|
|||
|
|
data_source = DataSource(
|
|||
|
|
name=data_source_data.name,
|
|||
|
|
type=data_source_data.type,
|
|||
|
|
description=data_source_data.description,
|
|||
|
|
config=data_source_data.config,
|
|||
|
|
user_id=current_user.id,
|
|||
|
|
status="active"
|
|||
|
|
)
|
|||
|
|
db.add(data_source)
|
|||
|
|
db.commit()
|
|||
|
|
db.refresh(data_source)
|
|||
|
|
return data_source
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.get("/{data_source_id}", response_model=DataSourceResponse)
|
|||
|
|
async def get_data_source(
|
|||
|
|
data_source_id: str,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""获取数据源详情"""
|
|||
|
|
data_source = db.query(DataSource).filter(
|
|||
|
|
DataSource.id == data_source_id,
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
).first()
|
|||
|
|
|
|||
|
|
if not data_source:
|
|||
|
|
raise NotFoundError("数据源", data_source_id)
|
|||
|
|
|
|||
|
|
return data_source
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.put("/{data_source_id}", response_model=DataSourceResponse)
|
|||
|
|
async def update_data_source(
|
|||
|
|
data_source_id: str,
|
|||
|
|
data_source_data: DataSourceUpdate,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""更新数据源"""
|
|||
|
|
data_source = db.query(DataSource).filter(
|
|||
|
|
DataSource.id == data_source_id,
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
).first()
|
|||
|
|
|
|||
|
|
if not data_source:
|
|||
|
|
raise NotFoundError("数据源", data_source_id)
|
|||
|
|
|
|||
|
|
if data_source_data.name is not None:
|
|||
|
|
data_source.name = data_source_data.name
|
|||
|
|
if data_source_data.description is not None:
|
|||
|
|
data_source.description = data_source_data.description
|
|||
|
|
if data_source_data.config is not None:
|
|||
|
|
data_source.config = data_source_data.config
|
|||
|
|
if data_source_data.status is not None:
|
|||
|
|
data_source.status = data_source_data.status
|
|||
|
|
|
|||
|
|
db.commit()
|
|||
|
|
db.refresh(data_source)
|
|||
|
|
return data_source
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.delete("/{data_source_id}", status_code=204)
|
|||
|
|
async def delete_data_source(
|
|||
|
|
data_source_id: str,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""删除数据源"""
|
|||
|
|
data_source = db.query(DataSource).filter(
|
|||
|
|
DataSource.id == data_source_id,
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
).first()
|
|||
|
|
|
|||
|
|
if not data_source:
|
|||
|
|
raise NotFoundError("数据源", data_source_id)
|
|||
|
|
|
|||
|
|
db.delete(data_source)
|
|||
|
|
db.commit()
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/{data_source_id}/test", status_code=200)
|
|||
|
|
async def test_data_source_connection(
|
|||
|
|
data_source_id: str,
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""测试数据源连接"""
|
|||
|
|
data_source = db.query(DataSource).filter(
|
|||
|
|
DataSource.id == data_source_id,
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
).first()
|
|||
|
|
|
|||
|
|
if not data_source:
|
|||
|
|
raise NotFoundError("数据源", data_source_id)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
connector = DataSourceConnector(data_source.type, data_source.config)
|
|||
|
|
result = connector.test_connection()
|
|||
|
|
|
|||
|
|
# 更新最后连接时间
|
|||
|
|
from datetime import datetime
|
|||
|
|
data_source.last_connected_at = datetime.utcnow()
|
|||
|
|
data_source.status = "active"
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"message": "连接成功",
|
|||
|
|
"details": result
|
|||
|
|
}
|
|||
|
|
except Exception as e:
|
|||
|
|
# 更新状态为error
|
|||
|
|
data_source.status = "error"
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"message": f"连接失败: {str(e)}",
|
|||
|
|
"error": str(e)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/{data_source_id}/query", status_code=200)
|
|||
|
|
async def query_data_source(
|
|||
|
|
data_source_id: str,
|
|||
|
|
query: Dict[str, Any],
|
|||
|
|
db: Session = Depends(get_db),
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""查询数据源"""
|
|||
|
|
data_source = db.query(DataSource).filter(
|
|||
|
|
DataSource.id == data_source_id,
|
|||
|
|
DataSource.user_id == current_user.id
|
|||
|
|
).first()
|
|||
|
|
|
|||
|
|
if not data_source:
|
|||
|
|
raise NotFoundError("数据源", data_source_id)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
connector = DataSourceConnector(data_source.type, data_source.config)
|
|||
|
|
result = connector.query(query)
|
|||
|
|
|
|||
|
|
# 更新最后连接时间
|
|||
|
|
from datetime import datetime
|
|||
|
|
data_source.last_connected_at = datetime.utcnow()
|
|||
|
|
data_source.status = "active"
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": True,
|
|||
|
|
"data": result
|
|||
|
|
}
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"查询数据源失败: {str(e)}")
|
|||
|
|
data_source.status = "error"
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"error": str(e)
|
|||
|
|
}
|