214 lines
6.2 KiB
Python
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)
|