Files
wxauto_api/config.py
T
2026-04-07 14:11:45 +08:00

214 lines
6.2 KiB
Python

# ///
# config.py
# 描述:系统配置管理,支持多环境配置
# 作者:AI Generated
# 创建日期:2026-04-05
# ///
import os
from typing import Dict, List, Optional
from pydantic import BaseModel
from pydantic_settings import BaseSettings
class NodeConfig(BaseModel):
node_id: str
name: str
api_url: str
api_key: str
enabled: bool = True
description: str = ""
group: str = "default"
class PluginConfig(BaseModel):
name: str
enabled: bool = True
config: Dict = {}
class WebhookConfig(BaseModel):
urls: List[str] = []
enabled: bool = False
timeout: int = 10
retry_times: int = 3
format: str = "wechat"
event_types: List[str] = ["api_error", "wechat_offline", "wechat_online", "node_offline", "node_online"]
class MonitorConfig(BaseModel):
enabled: bool = True
check_interval: int = 60
class RedisConfig(BaseModel):
url: str = "redis://localhost:6379/0"
db: int = 0
password: Optional[str] = None
class DatabaseConfig(BaseModel):
url: str = "postgresql://wxauto:wxauto@localhost:5432/wxauto"
pool_size: int = 10
max_overflow: int = 20
class Config:
populate_by_name = True
class Settings(BaseSettings):
app_name: str = "WXAuto Center"
host: str = "0.0.0.0"
port: int = 8080
debug: bool = False
secret_key: str = "wxauto-center-secret-key-change-in-production"
nodes: List[NodeConfig] = []
plugins: List[PluginConfig] = []
webhook: WebhookConfig = WebhookConfig()
monitor: MonitorConfig = MonitorConfig()
redis: RedisConfig = RedisConfig()
database: DatabaseConfig = DatabaseConfig()
cors_origins: List[str] = ["*"]
api_prefix: str = "/api/v1"
external_api_key: str = "your-external-api-key"
workers: int = 1
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
populate_by_name = True
extra = "allow"
env_nested_delimiter = "_"
_settings: Optional[Settings] = None
_config_file: str = "config.json"
def get_settings() -> Settings:
global _settings
if _settings is None:
_settings = Settings()
_settings.database.url = os.environ.get("DATABASE_URL", _settings.database.url)
_settings.redis.url = os.environ.get("REDIS_URL", _settings.redis.url)
return _settings
def update_settings(**kwargs):
global _settings
if _settings is None:
_settings = Settings()
for key, value in kwargs.items():
if hasattr(_settings, key):
setattr(_settings, key, value)
def load_config_from_file(config_file: str = "config.json") -> bool:
global _settings, _config_file
_config_file = config_file
if not os.path.exists(config_file):
return False
try:
import json
with open(config_file, "r", encoding="utf-8") as f:
data = json.load(f)
settings = get_settings()
settings.app_name = data.get("app_name", settings.app_name)
settings.host = data.get("host", settings.host)
settings.port = data.get("port", settings.port)
settings.debug = data.get("debug", settings.debug)
settings.secret_key = data.get("secret_key", settings.secret_key)
settings.cors_origins = data.get("cors_origins", settings.cors_origins)
settings.api_prefix = data.get("api_prefix", settings.api_prefix)
settings.external_api_key = data.get("external_api_key", settings.external_api_key)
plugins_data = data.get("plugins", [])
settings.plugins = [PluginConfig(**p) for p in plugins_data]
monitor_data = data.get("monitor", {})
settings.monitor = MonitorConfig(**monitor_data) if monitor_data else MonitorConfig()
settings.workers = data.get("workers", 1)
settings.database.url = os.environ.get("DATABASE_URL", settings.database.url)
settings.redis.url = os.environ.get("REDIS_URL", settings.redis.url)
return True
except Exception:
return False
def save_config_to_file(config_file: str = None):
global _config_file
if config_file:
_config_file = config_file
import json
settings = get_settings()
config_data = {
"app_name": settings.app_name,
"host": settings.host,
"port": settings.port,
"debug": settings.debug,
"secret_key": settings.secret_key,
"plugins": [p.model_dump() for p in settings.plugins],
"monitor": settings.monitor.model_dump(),
"workers": settings.workers,
"cors_origins": settings.cors_origins,
"api_prefix": settings.api_prefix,
"external_api_key": settings.external_api_key,
}
with open(_config_file, "w", encoding="utf-8") as f:
json.dump(config_data, f, indent=2, ensure_ascii=False)
class ConfigManager:
def __init__(self, config_file: str = "config.json"):
self.config_file = config_file
def get_node(self, node_id: str) -> Optional[NodeConfig]:
settings = get_settings()
for node in settings.nodes:
if node.node_id == node_id:
return node
return None
def get_all_nodes(self) -> List[NodeConfig]:
return get_settings().nodes
def get_enabled_nodes(self) -> List[NodeConfig]:
return [n for n in get_settings().nodes if n.enabled]
def add_node(self, node: NodeConfig) -> bool:
existing = self.get_node(node.node_id)
if existing:
return False
get_settings().nodes.append(node)
return True
def update_node(self, node_id: str, **kwargs) -> bool:
for i, node in enumerate(get_settings().nodes):
if node.node_id == node_id:
for key, value in kwargs.items():
if hasattr(node, key):
setattr(node, key, value)
return True
return False
def remove_node(self, node_id: str) -> bool:
for i, node in enumerate(get_settings().nodes):
if node.node_id == node_id:
get_settings().nodes.pop(i)
return True
return False
def save_config(self):
save_config_to_file(self.config_file)
def load_config(self) -> bool:
return load_config_from_file(self.config_file)