config.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """
  2. 配置 API 路由
  3. 提供数据源配置的读取和管理
  4. """
  5. from fastapi import APIRouter, HTTPException
  6. from pathlib import Path
  7. from typing import List, Dict, Any
  8. import yaml
  9. from loguru import logger
  10. from models.schemas import HealthResponse
  11. router = APIRouter(prefix="/api/config", tags=["config"])
  12. def _get_config_dir() -> Path:
  13. """获取配置目录路径"""
  14. return Path(__file__).parent.parent / "config" / "data_sources"
  15. def _resolve_template(template: str, variables: Dict[str, str]) -> str:
  16. """解析模板变量"""
  17. resolved = template
  18. for key, value in variables.items():
  19. resolved = resolved.replace(f"{{{{{key}}}}}", value)
  20. resolved = resolved.replace(f"{{{{ {key} }}}}", value)
  21. return resolved
  22. def _parse_data_source(config: Dict[str, Any]) -> Dict[str, Any]:
  23. """解析数据源配置,展开模板变量"""
  24. if 'data_source' not in config:
  25. return config
  26. ds = config['data_source']
  27. # 准备变量字典
  28. variables = {
  29. 'name': ds.get('name', ''),
  30. 'base_dir': ds.get('base_dir', '')
  31. }
  32. # 解析路径模板
  33. if 'json_dir' in ds:
  34. ds['json_dir'] = _resolve_template(ds['json_dir'], variables)
  35. if 'image_dir' in ds:
  36. ds['image_dir'] = _resolve_template(ds['image_dir'], variables)
  37. if 'output' in ds and 'directory' in ds['output']:
  38. ds['output']['directory'] = _resolve_template(ds['output']['directory'], variables)
  39. return ds
  40. @router.get("/data-sources")
  41. async def list_data_sources() -> Dict[str, List[Dict[str, Any]]]:
  42. """
  43. 列出所有预定义数据源
  44. Returns:
  45. 包含 sources 列表的字典,每个数据源包含 filename 字段(不含 .yaml 扩展名)
  46. """
  47. config_dir = _get_config_dir()
  48. sources = []
  49. try:
  50. if not config_dir.exists():
  51. logger.warning(f"配置目录不存在: {config_dir}")
  52. return {"sources": []}
  53. for yaml_file in sorted(config_dir.glob("*.yaml")):
  54. if yaml_file.name == "README.md":
  55. continue
  56. try:
  57. with open(yaml_file, 'r', encoding='utf-8') as f:
  58. config = yaml.safe_load(f)
  59. if config and 'data_source' in config:
  60. parsed = _parse_data_source(config)
  61. # 添加文件名作为唯一标识符(不含 .yaml 扩展名)
  62. filename = yaml_file.stem # 获取不含扩展名的文件名
  63. parsed['filename'] = filename
  64. sources.append(parsed)
  65. logger.debug(f"加载数据源配置: {yaml_file.name} (filename: {filename})")
  66. except Exception as e:
  67. logger.error(f"加载配置文件失败 {yaml_file}: {e}")
  68. continue
  69. logger.info(f"成功加载 {len(sources)} 个数据源配置")
  70. return {"sources": sources}
  71. except Exception as e:
  72. logger.exception(f"列出数据源失败: {e}")
  73. raise HTTPException(status_code=500, detail=f"列出数据源失败: {e}")
  74. @router.get("/data-sources/{filename}")
  75. async def get_data_source(filename: str) -> Dict[str, Any]:
  76. """
  77. 获取指定数据源配置
  78. Args:
  79. filename: 数据源配置文件名(不含 .yaml 扩展名)
  80. Returns:
  81. 数据源配置
  82. """
  83. config_dir = _get_config_dir()
  84. try:
  85. # 直接使用文件名查找配置文件
  86. yaml_file = config_dir / f"{filename}.yaml"
  87. if not yaml_file.exists():
  88. raise HTTPException(status_code=404, detail=f"数据源配置文件不存在: {filename}.yaml")
  89. with open(yaml_file, 'r', encoding='utf-8') as f:
  90. config = yaml.safe_load(f)
  91. if config and 'data_source' in config:
  92. parsed = _parse_data_source(config)
  93. # 添加文件名作为唯一标识符
  94. parsed['filename'] = filename
  95. return parsed
  96. else:
  97. raise HTTPException(status_code=404, detail=f"配置文件格式错误: {filename}.yaml")
  98. except HTTPException:
  99. raise
  100. except Exception as e:
  101. logger.exception(f"获取数据源失败: {e}")
  102. raise HTTPException(status_code=500, detail=f"获取数据源失败: {e}")
  103. @router.get("/health", response_model=HealthResponse)
  104. async def config_health_check():
  105. """配置服务健康检查"""
  106. return HealthResponse(
  107. status="ok",
  108. service="config"
  109. )