ai初期模板
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
配置初始化模块
|
||||
功能:
|
||||
1. 自动生成默认配置文件
|
||||
2. 多环境配置支持(dev/test/prod)
|
||||
3. 敏感信息加密存储
|
||||
4. 配置完整性检查与修复
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
import logging
|
||||
from cryptography.fernet import Fernet
|
||||
import hashlib
|
||||
|
||||
# 初始化日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger('config_init')
|
||||
|
||||
class ConfigInitializer:
|
||||
"""配置初始化工具类"""
|
||||
|
||||
def __init__(self, app_name: str = "intelligence_system"):
|
||||
self.system = platform.system().lower()
|
||||
self.app_name = app_name
|
||||
self.config_dir = self._get_config_dir()
|
||||
self.config_file = self.config_dir / "config.json"
|
||||
self.secret_key_file = self.config_dir / ".secret.key"
|
||||
self._fernet = None
|
||||
|
||||
# 确保配置目录存在
|
||||
self.config_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 设置文件权限(非Windows)
|
||||
if self.system != 'windows':
|
||||
os.chmod(self.config_dir, 0o700)
|
||||
|
||||
def _get_config_dir(self) -> Path:
|
||||
"""获取适合当前平台的配置目录路径"""
|
||||
if self.system == 'windows':
|
||||
return Path(os.environ['APPDATA']) / self.app_name
|
||||
elif self.system == 'darwin': # macOS
|
||||
return Path.home() / "Library" / "Application Support" / self.app_name
|
||||
else: # Linux及其他Unix-like
|
||||
xdg_config = os.getenv('XDG_CONFIG_HOME', '~/.config')
|
||||
return Path(xdg_config).expanduser() / self.app_name
|
||||
|
||||
def _init_encryption(self):
|
||||
"""初始化加密模块"""
|
||||
if not self.secret_key_file.exists():
|
||||
self.secret_key_file.write_bytes(Fernet.generate_key())
|
||||
if self.system != 'windows':
|
||||
self.secret_key_file.chmod(0o600) # 仅用户可读写
|
||||
|
||||
self._fernet = Fernet(self.secret_key_file.read_bytes())
|
||||
|
||||
def encrypt_value(self, plaintext: str) -> str:
|
||||
"""加密敏感信息"""
|
||||
if not self._fernet:
|
||||
self._init_encryption()
|
||||
return self._fernet.encrypt(plaintext.encode()).decode()
|
||||
|
||||
def decrypt_value(self, ciphertext: str) -> str:
|
||||
"""解密信息"""
|
||||
if not self._fernet:
|
||||
self._init_encryption()
|
||||
return self._fernet.decrypt(ciphertext.encode()).decode()
|
||||
|
||||
def _get_default_config(self) -> Dict[str, Any]:
|
||||
"""获取默认配置模板"""
|
||||
return {
|
||||
"system": {
|
||||
"env": "dev", # dev/test/prod
|
||||
"log_level": "INFO",
|
||||
"max_threads": max(1, os.cpu_count() or 4),
|
||||
"data_dir": str(self.config_dir / "data")
|
||||
},
|
||||
"api": {
|
||||
"newsapi": {
|
||||
"endpoint": "https://newsapi.org/v2",
|
||||
"key": "" # 需加密存储
|
||||
},
|
||||
"weibo": {
|
||||
"version": "2",
|
||||
"access_token": "" # 需加密存储
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"type": "sqlite",
|
||||
"path": str(self.config_dir / "data.db")
|
||||
},
|
||||
"network": {
|
||||
"timeout": 30,
|
||||
"retries": 3,
|
||||
"proxy": "" # 示例: http://user:pass@proxy:port
|
||||
}
|
||||
}
|
||||
|
||||
def _migrate_old_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""旧配置迁移(兼容性处理)"""
|
||||
# 示例:将旧版api_key迁移到新版结构
|
||||
if 'api_key' in config:
|
||||
config.setdefault('api', {})['newsapi'] = {
|
||||
'key': config.pop('api_key')
|
||||
}
|
||||
return config
|
||||
|
||||
def _validate_config(self, config: Dict[str, Any]) -> bool:
|
||||
"""验证配置完整性"""
|
||||
required_keys = {
|
||||
"system": ["env", "log_level"],
|
||||
"api/newsapi": ["endpoint"]
|
||||
}
|
||||
|
||||
for path, keys in required_keys.items():
|
||||
current = config
|
||||
for part in path.split('/'):
|
||||
current = current.get(part, {})
|
||||
if not isinstance(current, dict):
|
||||
return False
|
||||
|
||||
for key in keys:
|
||||
if key not in current:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _repair_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""自动修复缺失的配置项"""
|
||||
default_config = self._get_default_config()
|
||||
|
||||
def _merge(current, default):
|
||||
for key, value in default.items():
|
||||
if key not in current:
|
||||
current[key] = value
|
||||
elif isinstance(value, dict):
|
||||
_merge(current[key], value)
|
||||
return current
|
||||
|
||||
return _merge(config, default_config)
|
||||
|
||||
def init_config(self, force: bool = False) -> bool:
|
||||
"""
|
||||
初始化配置文件
|
||||
参数:
|
||||
force: 是否强制重新生成配置
|
||||
返回:
|
||||
bool: 是否创建了新配置
|
||||
"""
|
||||
config = None
|
||||
|
||||
# 已有配置文件且不强制重置
|
||||
if self.config_file.exists() and not force:
|
||||
try:
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
|
||||
# 配置迁移和修复
|
||||
config = self._migrate_old_config(config)
|
||||
if not self._validate_config(config):
|
||||
config = self._repair_config(config)
|
||||
logger.warning("自动修复不完整的配置文件")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"加载现有配置失败: {str(e)}")
|
||||
config = None
|
||||
|
||||
# 需要创建新配置
|
||||
if config is None:
|
||||
config = self._get_default_config()
|
||||
logger.info("创建新的配置文件")
|
||||
|
||||
# 加密敏感字段
|
||||
self._init_encryption()
|
||||
for field in [
|
||||
"api/newsapi/key",
|
||||
"api/weibo/access_token",
|
||||
"network/proxy"
|
||||
]:
|
||||
parts = field.split('/')
|
||||
current = config
|
||||
for part in parts[:-1]:
|
||||
current = current.setdefault(part, {})
|
||||
|
||||
if parts[-1] in current and current[parts[-1]]:
|
||||
current[parts[-1]] = self.encrypt_value(current[parts[-1]])
|
||||
|
||||
# 保存配置
|
||||
with open(self.config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# 设置文件权限(非Windows)
|
||||
if self.system != 'windows':
|
||||
os.chmod(self.config_file, 0o600)
|
||||
|
||||
return True
|
||||
|
||||
def get_config_hash(self) -> str:
|
||||
"""获取配置文件哈希值(用于检测变更)"""
|
||||
if not self.config_file.exists():
|
||||
return ""
|
||||
|
||||
with open(self.config_file, 'rb') as f:
|
||||
return hashlib.sha256(f.read()).hexdigest()
|
||||
|
||||
def create_env_specific_config(self, env: str = None) -> bool:
|
||||
"""
|
||||
创建环境特定配置
|
||||
参数:
|
||||
env: 环境类型(dev/test/prod)
|
||||
"""
|
||||
if not self.config_file.exists():
|
||||
self.init_config()
|
||||
|
||||
with open(self.config_file, 'r', encoding='utf-8') as f:
|
||||
base_config = json.load(f)
|
||||
|
||||
env = env or base_config['system']['env']
|
||||
env_config = {
|
||||
f"env_{env}": {
|
||||
"api": {
|
||||
"newsapi": {"endpoint": self._get_env_endpoint(env)}
|
||||
},
|
||||
"database": {
|
||||
"path": str(self.config_dir / f"data_{env}.db")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env_file = self.config_dir / f"config.{env}.json"
|
||||
with open(env_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(env_config, f, indent=2)
|
||||
|
||||
return True
|
||||
|
||||
def _get_env_endpoint(self, env: str) -> str:
|
||||
"""获取环境特定的API端点"""
|
||||
endpoints = {
|
||||
"dev": "http://dev-api.example.com",
|
||||
"test": "https://test-api.example.com",
|
||||
"prod": "https://api.example.com"
|
||||
}
|
||||
return endpoints.get(env, endpoints['dev'])
|
||||
|
||||
# 快捷初始化函数
|
||||
def init_app_config(app_name: str = None, force: bool = False) -> bool:
|
||||
"""
|
||||
快速初始化应用配置
|
||||
参数:
|
||||
app_name: 应用名称
|
||||
force: 是否强制重新初始化
|
||||
"""
|
||||
return ConfigInitializer(app_name).init_config(force)
|
||||
|
||||
# 测试代码
|
||||
if __name__ == "__main__":
|
||||
# 初始化配置
|
||||
initializer = ConfigInitializer()
|
||||
if initializer.init_config():
|
||||
print("配置文件已生成:", initializer.config_file)
|
||||
|
||||
# 创建环境配置示例
|
||||
initializer.create_env_specific_config("prod")
|
||||
print("生产环境配置已生成")
|
||||
|
||||
# 加密演示
|
||||
encrypted = initializer.encrypt_value("my_secret_key")
|
||||
print("加密示例:", encrypted)
|
||||
print("解密测试:", initializer.decrypt_value(encrypted))
|
||||
@@ -0,0 +1,56 @@
|
||||
[loggers]
|
||||
keys=root,data_collector,api_client,alert
|
||||
|
||||
[handlers]
|
||||
keys=consoleHandler,fileHandler,errorFileHandler
|
||||
|
||||
[formatters]
|
||||
keys=standardFormatter,detailedFormatter
|
||||
|
||||
[logger_root]
|
||||
level=INFO
|
||||
handlers=consoleHandler,fileHandler
|
||||
|
||||
[logger_data_collector]
|
||||
level=DEBUG
|
||||
handlers=fileHandler
|
||||
qualname=data_collector
|
||||
propagate=0
|
||||
|
||||
[logger_api_client]
|
||||
level=INFO
|
||||
handlers=fileHandler,errorFileHandler
|
||||
qualname=api_client
|
||||
propagate=0
|
||||
|
||||
[logger_alert]
|
||||
level=WARNING
|
||||
handlers=consoleHandler,errorFileHandler
|
||||
qualname=alert
|
||||
propagate=0
|
||||
|
||||
[handler_consoleHandler]
|
||||
class=StreamHandler
|
||||
level=INFO
|
||||
formatter=standardFormatter
|
||||
args=(sys.stdout,)
|
||||
|
||||
[handler_fileHandler]
|
||||
class=logging.handlers.TimedRotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=detailedFormatter
|
||||
args=('%(log_dir)s/application.log', 'midnight', 1, 30, 'utf-8')
|
||||
|
||||
[handler_errorFileHandler]
|
||||
class=logging.handlers.TimedRotatingFileHandler
|
||||
level=WARNING
|
||||
formatter=detailedFormatter
|
||||
args=('%(log_dir)s/error.log', 'midnight', 1, 90, 'utf-8')
|
||||
|
||||
[formatter_standardFormatter]
|
||||
format=%(asctime)s [%(levelname)-5s] %(name)s - %(message)s
|
||||
datefmt=%Y-%m-%d %H:%M:%S
|
||||
|
||||
[formatter_detailedFormatter]
|
||||
format=%(asctime)s [%(levelname)-5s] %(name)s (%(filename)s:%(lineno)d) - %(message)s
|
||||
datefmt=%Y-%m-%d %H:%M:%S
|
||||
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
系统配置模块
|
||||
功能:
|
||||
1. 支持多平台路径适配
|
||||
2. 环境变量与配置文件优先级管理
|
||||
3. 敏感信息加密存储
|
||||
4. 配置热更新检测
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
import dotenv
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
class ConfigManager:
|
||||
def __init__(self, app_name: str = "intelligence_system"):
|
||||
"""
|
||||
初始化配置管理器
|
||||
|
||||
参数:
|
||||
app_name: 应用名称(用于生成配置目录)
|
||||
"""
|
||||
self.system = platform.system().lower()
|
||||
self.app_name = app_name
|
||||
self._config = {}
|
||||
self._secret_key = None
|
||||
|
||||
# 初始化配置路径
|
||||
self.config_dir = self._get_config_dir()
|
||||
os.makedirs(self.config_dir, exist_ok=True)
|
||||
|
||||
# 加载配置顺序
|
||||
self._load_defaults()
|
||||
self._load_env_file()
|
||||
self._load_user_config()
|
||||
|
||||
# 初始化加密模块
|
||||
self._init_encryption()
|
||||
|
||||
def _get_config_dir(self) -> str:
|
||||
"""获取适合当前平台的配置目录"""
|
||||
if self.system == 'windows':
|
||||
return os.path.join(os.environ['APPDATA'], self.app_name)
|
||||
elif self.system == 'darwin': # macOS
|
||||
return os.path.expanduser(f"~/Library/Application Support/{self.app_name}")
|
||||
else: # Linux及其他Unix-like
|
||||
return os.path.expanduser(f"~/.config/{self.app_name}")
|
||||
|
||||
def _load_defaults(self):
|
||||
"""加载默认配置"""
|
||||
self._config = {
|
||||
"system": {
|
||||
"log_level": "INFO",
|
||||
"max_threads": os.cpu_count() or 4
|
||||
},
|
||||
"api": {
|
||||
"newsapi": {"endpoint": "https://newsapi.org/v2"},
|
||||
"weibo": {"version": "2"}
|
||||
},
|
||||
"paths": {
|
||||
"data_dir": os.path.join(self.config_dir, "data"),
|
||||
"cache_dir": os.path.join(self.config_dir, "cache")
|
||||
}
|
||||
}
|
||||
|
||||
def _load_env_file(self):
|
||||
"""加载.env环境变量文件"""
|
||||
env_path = Path(self.config_dir) / ".env"
|
||||
if env_path.exists():
|
||||
dotenv.load_dotenv(env_path)
|
||||
|
||||
# 环境变量覆盖配置
|
||||
if os.getenv("LOG_LEVEL"):
|
||||
self._config["system"]["log_level"] = os.getenv("LOG_LEVEL")
|
||||
|
||||
def _load_user_config(self):
|
||||
"""加载用户自定义配置"""
|
||||
config_file = Path(self.config_dir) / "config.json"
|
||||
try:
|
||||
if config_file.exists():
|
||||
with open(config_file, 'r', encoding='utf-8') as f:
|
||||
user_config = json.load(f)
|
||||
self._deep_update(self._config, user_config)
|
||||
except Exception as e:
|
||||
print(f"加载用户配置失败: {str(e)}")
|
||||
|
||||
def _init_encryption(self):
|
||||
"""初始化配置加密模块"""
|
||||
key_file = Path(self.config_dir) / ".secret.key"
|
||||
if key_file.exists():
|
||||
with open(key_file, 'rb') as f:
|
||||
self._secret_key = f.read()
|
||||
else:
|
||||
self._secret_key = Fernet.generate_key()
|
||||
with open(key_file, 'wb') as f:
|
||||
f.write(self._secret_key)
|
||||
key_file.chmod(0o600) # 设置密钥文件权限
|
||||
|
||||
def _deep_update(self, original: Dict, update: Dict) -> Dict:
|
||||
"""深度合并字典"""
|
||||
for key, value in update.items():
|
||||
if isinstance(value, dict) and key in original:
|
||||
original[key] = self._deep_update(original.get(key, {}), value)
|
||||
else:
|
||||
original[key] = value
|
||||
return original
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
"""
|
||||
获取配置项(支持点分路径)
|
||||
示例: get("api.newsapi.endpoint")
|
||||
"""
|
||||
keys = key.split('.')
|
||||
value = self._config
|
||||
try:
|
||||
for k in keys:
|
||||
value = value[k]
|
||||
return value
|
||||
except (KeyError, TypeError):
|
||||
return default
|
||||
|
||||
def set(self, key: str, value: Any, persist: bool = False):
|
||||
"""
|
||||
设置配置项
|
||||
参数:
|
||||
persist: 是否保存到用户配置文件
|
||||
"""
|
||||
keys = key.split('.')
|
||||
config_ref = self._config
|
||||
|
||||
for k in keys[:-1]:
|
||||
if k not in config_ref:
|
||||
config_ref[k] = {}
|
||||
config_ref = config_ref[k]
|
||||
|
||||
config_ref[keys[-1]] = value
|
||||
|
||||
if persist:
|
||||
self._save_user_config()
|
||||
|
||||
def encrypt_value(self, plaintext: str) -> str:
|
||||
"""加密敏感信息"""
|
||||
fernet = Fernet(self._secret_key)
|
||||
return fernet.encrypt(plaintext.encode()).decode()
|
||||
|
||||
def decrypt_value(self, ciphertext: str) -> str:
|
||||
"""解密敏感信息"""
|
||||
fernet = Fernet(self._secret_key)
|
||||
return fernet.decrypt(ciphertext.encode()).decode()
|
||||
|
||||
def _save_user_config(self):
|
||||
"""保存用户配置到文件"""
|
||||
config_file = Path(self.config_dir) / "config.json"
|
||||
with open(config_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(self._config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def reload(self):
|
||||
"""重新加载所有配置"""
|
||||
self._config = {}
|
||||
self._load_defaults()
|
||||
self._load_env_file()
|
||||
self._load_user_config()
|
||||
|
||||
# 全局配置实例
|
||||
config = ConfigManager()
|
||||
|
||||
# 快捷访问方法(兼容旧代码)
|
||||
def get_config(key: str, default: Optional[Any] = None) -> Any:
|
||||
return config.get(key, default)
|
||||
|
||||
def set_config(key: str, value: Any, persist: bool = False):
|
||||
config.set(key, value, persist)
|
||||
|
||||
# 测试代码
|
||||
if __name__ == "__main__":
|
||||
# 设置并保存API密钥(自动加密)
|
||||
api_key = "your_newsapi_key_here"
|
||||
encrypted_key = config.encrypt_value(api_key)
|
||||
config.set("api.newsapi.key", encrypted_key, persist=True)
|
||||
|
||||
# 获取配置示例
|
||||
print(f"日志级别: {config.get('system.log_level')}")
|
||||
print(f"NewsAPI端点: {config.get('api.newsapi.endpoint')}")
|
||||
|
||||
# 解密敏感信息
|
||||
stored_key = config.get("api.newsapi.key")
|
||||
if stored_key:
|
||||
print(f"解密后的API密钥: {config.decrypt_value(stored_key)}")
|
||||
Reference in New Issue
Block a user