diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index 24db7ea8..f843577d 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -32,11 +32,13 @@ from app.helper.subscribe import SubscribeHelper from app.helper.system import SystemHelper from app.log import logger from app.scheduler import Scheduler -from app.schemas.types import SystemConfigKey +from app.schemas import ConfigChangeEventData +from app.schemas.types import SystemConfigKey, EventType from app.utils.crypto import HashUtils from app.utils.http import RequestUtils from app.utils.security import SecurityUtils from app.utils.url import UrlUtils +from core.event import eventmanager from version import APP_VERSION router = APIRouter() @@ -218,7 +220,7 @@ def set_env_setting(env: dict, result = settings.update_settings(env=env) # 统计成功和失败的结果 success_updates = {k: v for k, v in result.items() if v[0]} - failed_updates = {k: v for k, v in result.items() if not v[0]} + failed_updates = {k: v for k, v in result.items() if v[0] is False} if failed_updates: return schemas.Response( @@ -230,6 +232,15 @@ def set_env_setting(env: dict, } ) + if success_updates: + for key in success_updates.keys(): + # 发送配置变更事件 + eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( + key=key, + value=getattr(settings, key, None), + change_type="update" + )) + return schemas.Response( success=True, message="所有配置项更新成功", @@ -283,12 +294,26 @@ def set_setting(key: str, value: Union[list, dict, bool, int, str] = None, """ if hasattr(settings, key): success, message = settings.update_setting(key=key, value=value) + if success: + # 发送配置变更事件 + eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( + key=key, + value=value, + change_type="update" + )) return schemas.Response(success=success, message=message) elif key in {item.value for item in SystemConfigKey}: if isinstance(value, list): value = list(filter(None, value)) value = value if value else None - SystemConfigOper().set(key, value) + success = SystemConfigOper().set(key, value) + if success: + # 发送配置变更事件 + eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( + key=key, + value=value, + change_type="update" + )) return schemas.Response(success=True) else: return schemas.Response(success=False, message=f"配置项 '{key}' 不存在") diff --git a/app/core/config.py b/app/core/config.py index 01034047..fa970b68 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -13,8 +13,6 @@ from pydantic import BaseModel, BaseSettings, validator, Field from app.log import logger, log_settings, LogConfigModel from app.utils.system import SystemUtils from app.utils.url import UrlUtils -from app.schemas.types import EventType -from app.schemas import ConfigChangeEventData class ConfigModel(BaseModel): @@ -391,8 +389,6 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel): f"配置项 '{field_name}' 的值 '{value}' 无法转换成正确的类型,使用默认值 '{default}',错误信息: {e}") return default, True - # TODO[pydantic]: We couldn't refactor the `validator`, please replace it by `field_validator` manually. - # Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-validators for more information. @validator('*', pre=True, always=True) def generic_type_validator(cls, value: Any, field): # noqa """ @@ -435,9 +431,12 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel): logger.info(f"配置项 '{field.name}' 已自动修正并写入到 'app.env' 文件") return True, message - def update_setting(self, key: str, value: Any) -> Tuple[bool, str]: + def update_setting(self, key: str, value: Any) -> Tuple[Optional[bool], str]: """ 更新单个配置项 + :param key: 配置项的名称 + :param value: 配置项的新值 + :return: (是否成功 True 成功/False 失败/None 无需更新, 错误信息) """ if not hasattr(self, key): return False, f"配置项 '{key}' 不存在" @@ -448,32 +447,25 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel): if field.name == "API_TOKEN": converted_value, needs_update = self.validate_api_token(value, original_value) else: - converted_value, needs_update = self.generic_type_converter(value, original_value, field.type_, - field.default, key) + converted_value, needs_update = self.generic_type_converter(value, + original_value, + field.type_, + field.default, + key) # 如果没有抛出异常,则统一使用 converted_value 进行更新 if needs_update or str(value) != str(converted_value): success, message = self.update_env_config(field, value, converted_value) # 仅成功更新配置时,才更新内存 if success: - old_value = getattr(self, key) setattr(self, key, converted_value) if hasattr(log_settings, key): setattr(log_settings, key, converted_value) - # 发送配置变更通知 - from app.core.event import eventmanager - eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( - key=key, - old_value=old_value, - new_value=converted_value, - change_type="update" - )) - return success, message - return True, "" + return None, "" except Exception as e: return False, str(e) - def update_settings(self, env: Dict[str, Any]) -> Dict[str, Tuple[bool, str]]: + def update_settings(self, env: Dict[str, Any]) -> Dict[str, Tuple[Optional[bool], str]]: """ 更新多个配置项 """ diff --git a/app/db/systemconfig_oper.py b/app/db/systemconfig_oper.py index 531b5ac0..42b28626 100644 --- a/app/db/systemconfig_oper.py +++ b/app/db/systemconfig_oper.py @@ -1,10 +1,9 @@ -from typing import Any, Union +from typing import Any, Union, Optional from app.db import DbOper from app.db.models.systemconfig import SystemConfig -from app.schemas.types import SystemConfigKey, EventType +from app.schemas.types import SystemConfigKey from app.utils.singleton import Singleton -from app.schemas import ConfigChangeEventData class SystemConfigOper(DbOper, metaclass=Singleton): @@ -19,9 +18,12 @@ class SystemConfigOper(DbOper, metaclass=Singleton): for item in SystemConfig.list(self._db): self.__SYSTEMCONF[item.key] = item.value - def set(self, key: Union[str, SystemConfigKey], value: Any): + def set(self, key: Union[str, SystemConfigKey], value: Any) -> Optional[bool]: """ 设置系统设置 + :param key: 配置键 + :param value: 配置值 + :return: 是否设置成功(True 成功/False 失败/None 无需更新) """ if isinstance(key, SystemConfigKey): key = key.value @@ -31,38 +33,17 @@ class SystemConfigOper(DbOper, metaclass=Singleton): self.__SYSTEMCONF[key] = value conf = SystemConfig.get_by_key(self._db, key) if conf: - if value: - conf.update(self._db, {"value": value}) - # 发送配置变更通知 - if old_value != value: - from app.core.event import eventmanager - eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( - key=key, - old_value=old_value, - new_value=value, - change_type="update" - )) - else: - conf.delete(self._db, conf.id) - # 发送配置删除通知 - from app.core.event import eventmanager - eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( - key=key, - old_value=old_value, - new_value=value, - change_type="delete" - )) + if old_value != value: + if value: + conf.update(self._db, {"value": value}) + else: + conf.delete(self._db, conf.id) + return True + return None else: conf = SystemConfig(key=key, value=value) conf.create(self._db) - # 发送配置变更通知 - from app.core.event import eventmanager - eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( - key=key, - old_value=old_value, - new_value=value, - change_type="add" - )) + return True def get(self, key: Union[str, SystemConfigKey] = None) -> Any: """ @@ -80,26 +61,18 @@ class SystemConfigOper(DbOper, metaclass=Singleton): """ return self.__SYSTEMCONF or {} - def delete(self, key: Union[str, SystemConfigKey]): + def delete(self, key: Union[str, SystemConfigKey]) -> bool: """ 删除系统设置 """ if isinstance(key, SystemConfigKey): key = key.value # 更新内存 - old_value = self.__SYSTEMCONF.pop(key, None) + self.__SYSTEMCONF.pop(key, None) # 写入数据库 conf = SystemConfig.get_by_key(self._db, key) if conf: conf.delete(self._db, conf.id) - # 发送配置变更通知 - from app.core.event import eventmanager - eventmanager.send_event(etype=EventType.ConfigChanged, data=ConfigChangeEventData( - key=key, - old_value=old_value, - new_value=None, - change_type="delete" - )) return True def __del__(self): diff --git a/app/schemas/event.py b/app/schemas/event.py index 0efe01b1..a880f826 100644 --- a/app/schemas/event.py +++ b/app/schemas/event.py @@ -27,8 +27,7 @@ class ConfigChangeEventData(BaseEventData): ConfigChange 事件的数据模型 """ key: str = Field(..., description="配置项的键") - old_value: Optional[Any] = Field(default=None, description="配置项的旧值") - new_value: Optional[Any] = Field(default=None, description="配置项的新值") + value: Optional[Any] = Field(default=None, description="配置项的新值") change_type: str = Field(default="update", description="配置项的变更类型,如 'add', 'update', 'delete'")