""" 配置 API 路由 提供数据源配置的读取和管理 """ from fastapi import APIRouter, HTTPException from pathlib import Path from typing import List, Dict, Any import yaml from loguru import logger from models.schemas import HealthResponse router = APIRouter(prefix="/api/config", tags=["config"]) def _get_config_dir() -> Path: """获取配置目录路径""" return Path(__file__).parent.parent / "config" / "data_sources" def _resolve_template(template: str, variables: Dict[str, str]) -> str: """解析模板变量""" resolved = template for key, value in variables.items(): resolved = resolved.replace(f"{{{{{key}}}}}", value) resolved = resolved.replace(f"{{{{ {key} }}}}", value) return resolved def _parse_data_source(config: Dict[str, Any]) -> Dict[str, Any]: """解析数据源配置,展开模板变量""" if 'data_source' not in config: return config ds = config['data_source'] # 准备变量字典 variables = { 'name': ds.get('name', ''), 'base_dir': ds.get('base_dir', '') } # 解析路径模板 if 'json_dir' in ds: ds['json_dir'] = _resolve_template(ds['json_dir'], variables) if 'image_dir' in ds: ds['image_dir'] = _resolve_template(ds['image_dir'], variables) if 'output' in ds and 'directory' in ds['output']: ds['output']['directory'] = _resolve_template(ds['output']['directory'], variables) return ds @router.get("/data-sources") async def list_data_sources() -> Dict[str, List[Dict[str, Any]]]: """ 列出所有预定义数据源 Returns: 包含 sources 列表的字典,每个数据源包含 filename 字段(不含 .yaml 扩展名) """ config_dir = _get_config_dir() sources = [] try: if not config_dir.exists(): logger.warning(f"配置目录不存在: {config_dir}") return {"sources": []} for yaml_file in sorted(config_dir.glob("*.yaml")): if yaml_file.name == "README.md": continue try: with open(yaml_file, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) if config and 'data_source' in config: parsed = _parse_data_source(config) # 添加文件名作为唯一标识符(不含 .yaml 扩展名) filename = yaml_file.stem # 获取不含扩展名的文件名 parsed['filename'] = filename sources.append(parsed) logger.debug(f"加载数据源配置: {yaml_file.name} (filename: {filename})") except Exception as e: logger.error(f"加载配置文件失败 {yaml_file}: {e}") continue logger.info(f"成功加载 {len(sources)} 个数据源配置") return {"sources": sources} except Exception as e: logger.exception(f"列出数据源失败: {e}") raise HTTPException(status_code=500, detail=f"列出数据源失败: {e}") @router.get("/data-sources/{filename}") async def get_data_source(filename: str) -> Dict[str, Any]: """ 获取指定数据源配置 Args: filename: 数据源配置文件名(不含 .yaml 扩展名) Returns: 数据源配置 """ config_dir = _get_config_dir() try: # 直接使用文件名查找配置文件 yaml_file = config_dir / f"{filename}.yaml" if not yaml_file.exists(): raise HTTPException(status_code=404, detail=f"数据源配置文件不存在: {filename}.yaml") with open(yaml_file, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) if config and 'data_source' in config: parsed = _parse_data_source(config) # 添加文件名作为唯一标识符 parsed['filename'] = filename return parsed else: raise HTTPException(status_code=404, detail=f"配置文件格式错误: {filename}.yaml") except HTTPException: raise except Exception as e: logger.exception(f"获取数据源失败: {e}") raise HTTPException(status_code=500, detail=f"获取数据源失败: {e}") @router.get("/health", response_model=HealthResponse) async def config_health_check(): """配置服务健康检查""" return HealthResponse( status="ok", service="config" )