2025-08-03 16:50:56 +08:00
|
|
|
|
import json
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from typing import Any, Dict, Optional
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
from loguru import logger
|
|
|
|
|
|
from pydantic import ValidationError
|
|
|
|
|
|
|
2025-11-20 20:44:25 +08:00
|
|
|
|
from loveace.config.settings import Settings
|
2025-08-03 16:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConfigManager:
|
|
|
|
|
|
"""配置文件管理器"""
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def __init__(self, config_file: str = "config.json"):
|
|
|
|
|
|
self.config_file = Path(config_file)
|
|
|
|
|
|
self._settings: Optional[Settings] = None
|
|
|
|
|
|
self._ensure_config_dir()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def _ensure_config_dir(self):
|
|
|
|
|
|
"""确保配置文件目录存在"""
|
|
|
|
|
|
self.config_file.parent.mkdir(parents=True, exist_ok=True)
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def _create_default_config(self) -> Settings:
|
|
|
|
|
|
"""创建默认配置"""
|
|
|
|
|
|
logger.info("正在创建默认配置文件...")
|
|
|
|
|
|
return Settings()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def _save_config(self, settings: Settings):
|
|
|
|
|
|
"""保存配置到文件"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
config_dict = settings.dict()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
with open(self.config_file, "w", encoding="utf-8") as f:
|
2025-08-03 16:50:56 +08:00
|
|
|
|
json.dump(config_dict, f, indent=2, ensure_ascii=False)
|
|
|
|
|
|
logger.info(f"配置已保存到 {self.config_file}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"保存配置文件失败: {e}")
|
|
|
|
|
|
raise
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def _load_config(self) -> Settings:
|
|
|
|
|
|
"""从文件加载配置"""
|
|
|
|
|
|
if not self.config_file.exists():
|
|
|
|
|
|
logger.warning(f"配置文件 {self.config_file} 不存在,将创建默认配置")
|
|
|
|
|
|
settings = self._create_default_config()
|
|
|
|
|
|
self._save_config(settings)
|
|
|
|
|
|
return settings
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
try:
|
2025-11-20 20:44:25 +08:00
|
|
|
|
with open(self.config_file, "r", encoding="utf-8") as f:
|
2025-08-03 16:50:56 +08:00
|
|
|
|
config_data = json.load(f)
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 验证并创建Settings对象
|
|
|
|
|
|
settings = Settings(**config_data)
|
|
|
|
|
|
logger.info(f"成功加载配置文件: {self.config_file}")
|
|
|
|
|
|
return settings
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
except json.JSONDecodeError as e:
|
|
|
|
|
|
logger.error(f"配置文件JSON格式错误: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
except ValidationError as e:
|
|
|
|
|
|
logger.error(f"配置文件验证失败: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"加载配置文件失败: {e}")
|
|
|
|
|
|
raise
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def get_settings(self) -> Settings:
|
|
|
|
|
|
"""获取配置设置"""
|
|
|
|
|
|
if self._settings is None:
|
|
|
|
|
|
self._settings = self._load_config()
|
|
|
|
|
|
return self._settings
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def reload_config(self) -> Settings:
|
|
|
|
|
|
"""重新加载配置"""
|
|
|
|
|
|
logger.info("正在重新加载配置...")
|
|
|
|
|
|
self._settings = self._load_config()
|
|
|
|
|
|
return self._settings
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def update_config(self, **kwargs) -> Settings:
|
|
|
|
|
|
"""更新配置"""
|
|
|
|
|
|
settings = self.get_settings()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 创建新的配置字典
|
|
|
|
|
|
config_dict = settings.dict()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 更新指定的配置项
|
|
|
|
|
|
for key, value in kwargs.items():
|
2025-11-20 20:44:25 +08:00
|
|
|
|
if "." in key:
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 支持嵌套键,如 'database.url'
|
2025-11-20 20:44:25 +08:00
|
|
|
|
keys = key.split(".")
|
2025-08-03 16:50:56 +08:00
|
|
|
|
current = config_dict
|
|
|
|
|
|
for k in keys[:-1]:
|
|
|
|
|
|
if k not in current:
|
|
|
|
|
|
current[k] = {}
|
|
|
|
|
|
current = current[k]
|
|
|
|
|
|
current[keys[-1]] = value
|
|
|
|
|
|
else:
|
|
|
|
|
|
config_dict[key] = value
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 验证更新后的配置
|
|
|
|
|
|
new_settings = Settings(**config_dict)
|
|
|
|
|
|
self._save_config(new_settings)
|
|
|
|
|
|
self._settings = new_settings
|
|
|
|
|
|
logger.info("配置更新成功")
|
|
|
|
|
|
return new_settings
|
|
|
|
|
|
except ValidationError as e:
|
|
|
|
|
|
logger.error(f"配置更新失败,验证错误: {e}")
|
|
|
|
|
|
raise
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def validate_config(self) -> bool:
|
|
|
|
|
|
"""验证配置完整性"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
settings = self.get_settings()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 检查关键配置项
|
|
|
|
|
|
issues = []
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 检查数据库配置
|
|
|
|
|
|
if not settings.database.url:
|
|
|
|
|
|
issues.append("数据库URL未配置")
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 检查S3配置(如果需要使用)
|
|
|
|
|
|
if settings.s3.bucket_name and not settings.s3.access_key_id:
|
|
|
|
|
|
issues.append("S3配置不完整:缺少access_key_id")
|
|
|
|
|
|
if settings.s3.bucket_name and not settings.s3.secret_access_key:
|
|
|
|
|
|
issues.append("S3配置不完整:缺少secret_access_key")
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 检查日志配置
|
|
|
|
|
|
log_dir = Path(settings.log.file_path).parent
|
|
|
|
|
|
if not log_dir.exists():
|
|
|
|
|
|
try:
|
|
|
|
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
logger.info(f"创建日志目录: {log_dir}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
issues.append(f"无法创建日志目录 {log_dir}: {e}")
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
if issues:
|
|
|
|
|
|
logger.warning("配置验证发现问题:")
|
|
|
|
|
|
for issue in issues:
|
|
|
|
|
|
logger.warning(f" - {issue}")
|
|
|
|
|
|
return False
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
logger.info("配置验证通过")
|
|
|
|
|
|
return True
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"配置验证失败: {e}")
|
|
|
|
|
|
return False
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def get_config_summary(self) -> Dict[str, Any]:
|
|
|
|
|
|
"""获取配置摘要(隐藏敏感信息)"""
|
|
|
|
|
|
settings = self.get_settings()
|
|
|
|
|
|
config_dict = settings.dict()
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
# 隐藏敏感信息
|
2025-11-20 20:44:25 +08:00
|
|
|
|
sensitive_keys = ["database.url", "s3.access_key_id", "s3.secret_access_key"]
|
|
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
def hide_sensitive(data: Dict[str, Any], keys: list, prefix: str = ""):
|
|
|
|
|
|
for key, value in data.items():
|
|
|
|
|
|
current_key = f"{prefix}.{key}" if prefix else key
|
|
|
|
|
|
if current_key in sensitive_keys:
|
|
|
|
|
|
if isinstance(value, str) and value:
|
|
|
|
|
|
data[key] = value[:8] + "..." if len(value) > 8 else "***"
|
|
|
|
|
|
elif isinstance(value, dict):
|
|
|
|
|
|
hide_sensitive(value, keys, current_key)
|
2025-11-20 20:44:25 +08:00
|
|
|
|
|
2025-08-03 16:50:56 +08:00
|
|
|
|
summary = config_dict.copy()
|
|
|
|
|
|
hide_sensitive(summary, sensitive_keys)
|
|
|
|
|
|
return summary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 全局配置管理器实例
|
2025-11-20 20:44:25 +08:00
|
|
|
|
config_manager = ConfigManager()
|