chore: update core to v1.1.0 (hooks plugin system)
This commit is contained in:
92
core/backend/app/hooks/loader.py
Normal file
92
core/backend/app/hooks/loader.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""客户钩子加载器 — 从 customers/{CUSTOMER}/hooks/ 动态加载客户自定义钩子
|
||||
|
||||
加载逻辑:
|
||||
1. 读取 CUSTOMER 环境变量
|
||||
2. 查找 customers/{CUSTOMER}/hooks/__init__.py
|
||||
3. 导入并返回 hooks 实例
|
||||
4. 找不到则返回默认空钩子(所有方法为空操作)
|
||||
"""
|
||||
import importlib
|
||||
import importlib.util
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from typing import Optional
|
||||
|
||||
from app.hooks.base import CustomerHooks
|
||||
from app.core.config import get_settings
|
||||
|
||||
logger = logging.getLogger("hooks.loader")
|
||||
|
||||
_hooks_instance: Optional[CustomerHooks] = None
|
||||
|
||||
|
||||
def _find_hooks_dir() -> Optional[str]:
|
||||
"""Find the customer hooks directory, searching multiple locations."""
|
||||
settings = get_settings()
|
||||
customer = settings.CUSTOMER
|
||||
config_path = settings.customer_config_path
|
||||
|
||||
hooks_dir = os.path.join(config_path, "hooks")
|
||||
if os.path.isdir(hooks_dir) and os.path.exists(os.path.join(hooks_dir, "__init__.py")):
|
||||
return hooks_dir
|
||||
return None
|
||||
|
||||
|
||||
def _load_hooks_from_dir(hooks_dir: str) -> Optional[CustomerHooks]:
|
||||
"""Load hooks module from a directory path."""
|
||||
try:
|
||||
init_path = os.path.join(hooks_dir, "__init__.py")
|
||||
spec = importlib.util.spec_from_file_location("customer_hooks", init_path)
|
||||
if spec and spec.loader:
|
||||
# Add parent dir to sys.path so relative imports work
|
||||
parent_dir = os.path.dirname(os.path.dirname(hooks_dir))
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
hooks = getattr(module, "hooks", None)
|
||||
if isinstance(hooks, CustomerHooks):
|
||||
return hooks
|
||||
else:
|
||||
logger.warning(
|
||||
f"Customer hooks module loaded but 'hooks' attribute is not a CustomerHooks instance"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load customer hooks: {e}", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
def get_hooks() -> CustomerHooks:
|
||||
"""获取当前客户的钩子实例。线程安全,全局单例。
|
||||
|
||||
Returns:
|
||||
CustomerHooks: 客户钩子实例,如果客户没有自定义钩子则返回默认空钩子
|
||||
"""
|
||||
global _hooks_instance
|
||||
if _hooks_instance is not None:
|
||||
return _hooks_instance
|
||||
|
||||
settings = get_settings()
|
||||
hooks_dir = _find_hooks_dir()
|
||||
|
||||
if hooks_dir:
|
||||
loaded = _load_hooks_from_dir(hooks_dir)
|
||||
if loaded:
|
||||
logger.info(f"Loaded customer hooks for '{settings.CUSTOMER}' from {hooks_dir}")
|
||||
_hooks_instance = loaded
|
||||
return _hooks_instance
|
||||
|
||||
logger.info(f"No custom hooks for '{settings.CUSTOMER}', using defaults")
|
||||
_hooks_instance = CustomerHooks()
|
||||
return _hooks_instance
|
||||
|
||||
|
||||
def reload_hooks() -> CustomerHooks:
|
||||
"""重新加载客户钩子(开发时热重载用)"""
|
||||
global _hooks_instance
|
||||
_hooks_instance = None
|
||||
return get_hooks()
|
||||
Reference in New Issue
Block a user