158 lines
5.9 KiB
Python
158 lines
5.9 KiB
Python
"""
|
||
配置管理模块
|
||
支持从YAML文件加载配置,支持环境变量覆盖
|
||
"""
|
||
import os
|
||
import yaml
|
||
from typing import Dict, Any
|
||
|
||
|
||
class Config:
|
||
"""配置类"""
|
||
|
||
def __init__(self, config_dict: Dict[str, Any]):
|
||
self._config = config_dict
|
||
|
||
def get(self, key: str, default=None):
|
||
"""获取配置值,支持点号分隔的嵌套键"""
|
||
keys = key.split('.')
|
||
value = self._config
|
||
|
||
for k in keys:
|
||
if isinstance(value, dict):
|
||
value = value.get(k)
|
||
if value is None:
|
||
return default
|
||
else:
|
||
return default
|
||
|
||
return value
|
||
|
||
def get_dict(self, key: str) -> Dict[str, Any]:
|
||
"""获取配置字典"""
|
||
value = self.get(key)
|
||
return value if isinstance(value, dict) else {}
|
||
|
||
def get_int(self, key: str, default: int = 0) -> int:
|
||
"""获取整数配置"""
|
||
value = self.get(key, default)
|
||
try:
|
||
return int(value)
|
||
except (ValueError, TypeError):
|
||
return default
|
||
|
||
def get_bool(self, key: str, default: bool = False) -> bool:
|
||
"""获取布尔配置"""
|
||
value = self.get(key, default)
|
||
if isinstance(value, bool):
|
||
return value
|
||
if isinstance(value, str):
|
||
return value.lower() in ('true', 'yes', '1', 'on')
|
||
return bool(value)
|
||
|
||
def get_str(self, key: str, default: str = '') -> str:
|
||
"""获取字符串配置"""
|
||
value = self.get(key, default)
|
||
return str(value) if value is not None else default
|
||
|
||
|
||
def load_config(env: str = None) -> Config:
|
||
"""
|
||
加载配置文件
|
||
|
||
Args:
|
||
env: 环境名称,可选值: dev, prod
|
||
如果不指定,从环境变量 ENV 读取,默认为 dev
|
||
|
||
Returns:
|
||
Config对象
|
||
"""
|
||
# 确定环境
|
||
if env is None:
|
||
env = os.getenv('ENV', 'dev')
|
||
|
||
# 配置文件路径
|
||
config_file = f'config.{env}.yaml'
|
||
config_path = os.path.join(os.path.dirname(__file__), config_file)
|
||
|
||
if not os.path.exists(config_path):
|
||
raise FileNotFoundError(f"配置文件不存在: {config_path}")
|
||
|
||
# 加载YAML配置
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
config_dict = yaml.safe_load(f)
|
||
|
||
# 环境变量覆盖(支持常用配置)
|
||
# 数据库配置
|
||
if os.getenv('DB_HOST'):
|
||
config_dict.setdefault('database', {})['host'] = os.getenv('DB_HOST')
|
||
if os.getenv('DB_PORT'):
|
||
config_dict.setdefault('database', {})['port'] = int(os.getenv('DB_PORT'))
|
||
if os.getenv('DB_USER'):
|
||
config_dict.setdefault('database', {})['username'] = os.getenv('DB_USER')
|
||
if os.getenv('DB_PASSWORD'):
|
||
config_dict.setdefault('database', {})['password'] = os.getenv('DB_PASSWORD')
|
||
if os.getenv('DB_NAME'):
|
||
config_dict.setdefault('database', {})['dbname'] = os.getenv('DB_NAME')
|
||
|
||
# Redis配置
|
||
if os.getenv('REDIS_HOST'):
|
||
config_dict.setdefault('redis', {})['host'] = os.getenv('REDIS_HOST')
|
||
if os.getenv('REDIS_PORT'):
|
||
config_dict.setdefault('redis', {})['port'] = int(os.getenv('REDIS_PORT'))
|
||
if os.getenv('REDIS_PASSWORD'):
|
||
config_dict.setdefault('redis', {})['password'] = os.getenv('REDIS_PASSWORD')
|
||
if os.getenv('REDIS_DB'):
|
||
config_dict.setdefault('redis', {})['db'] = int(os.getenv('REDIS_DB'))
|
||
|
||
# 调度器配置
|
||
if os.getenv('SCHEDULER_ENABLED'):
|
||
config_dict.setdefault('scheduler', {})['enabled'] = os.getenv('SCHEDULER_ENABLED').lower() == 'true'
|
||
if os.getenv('SCHEDULER_CRON'):
|
||
config_dict.setdefault('scheduler', {})['cron'] = os.getenv('SCHEDULER_CRON')
|
||
if os.getenv('SCHEDULER_MAX_CONCURRENT'):
|
||
config_dict.setdefault('scheduler', {})['max_concurrent'] = int(os.getenv('SCHEDULER_MAX_CONCURRENT'))
|
||
if os.getenv('SCHEDULER_PUBLISH_TIMEOUT'):
|
||
config_dict.setdefault('scheduler', {})['publish_timeout'] = int(os.getenv('SCHEDULER_PUBLISH_TIMEOUT'))
|
||
if os.getenv('SCHEDULER_MAX_ARTICLES_PER_USER_PER_RUN'):
|
||
config_dict.setdefault('scheduler', {})['max_articles_per_user_per_run'] = int(os.getenv('SCHEDULER_MAX_ARTICLES_PER_USER_PER_RUN'))
|
||
if os.getenv('SCHEDULER_MAX_FAILURES_PER_USER_PER_RUN'):
|
||
config_dict.setdefault('scheduler', {})['max_failures_per_user_per_run'] = int(os.getenv('SCHEDULER_MAX_FAILURES_PER_USER_PER_RUN'))
|
||
if os.getenv('SCHEDULER_MAX_DAILY_ARTICLES_PER_USER'):
|
||
config_dict.setdefault('scheduler', {})['max_daily_articles_per_user'] = int(os.getenv('SCHEDULER_MAX_DAILY_ARTICLES_PER_USER'))
|
||
if os.getenv('SCHEDULER_MAX_HOURLY_ARTICLES_PER_USER'):
|
||
config_dict.setdefault('scheduler', {})['max_hourly_articles_per_user'] = int(os.getenv('SCHEDULER_MAX_HOURLY_ARTICLES_PER_USER'))
|
||
|
||
# 代理池配置
|
||
if os.getenv('PROXY_POOL_ENABLED'):
|
||
config_dict.setdefault('proxy_pool', {})['enabled'] = os.getenv('PROXY_POOL_ENABLED').lower() == 'true'
|
||
if os.getenv('PROXY_POOL_API_URL'):
|
||
config_dict.setdefault('proxy_pool', {})['api_url'] = os.getenv('PROXY_POOL_API_URL')
|
||
|
||
print(f"[配置] 已加载配置文件: {config_file}")
|
||
print(f"[配置] 环境: {env}")
|
||
print(f"[配置] 数据库: {config_dict.get('database', {}).get('host')}:{config_dict.get('database', {}).get('port')}")
|
||
print(f"[配置] Redis: {config_dict.get('redis', {}).get('host')}:{config_dict.get('redis', {}).get('port')}")
|
||
print(f"[配置] 调度器: {'启用' if config_dict.get('scheduler', {}).get('enabled') else '禁用'}")
|
||
|
||
return Config(config_dict)
|
||
|
||
|
||
# 全局配置对象
|
||
app_config: Config = None
|
||
|
||
|
||
def init_config(env: str = None):
|
||
"""初始化全局配置"""
|
||
global app_config
|
||
app_config = load_config(env)
|
||
return app_config
|
||
|
||
|
||
def get_config() -> Config:
|
||
"""获取全局配置对象"""
|
||
global app_config
|
||
if app_config is None:
|
||
app_config = load_config()
|
||
return app_config
|