feat(ConfigCenter): ensure runtime settings sync with env

This commit is contained in:
InfinityPacer
2024-10-20 23:46:07 +08:00
parent 2291946fe8
commit dc925dfa1b

View File

@@ -1,8 +1,5 @@
import copy
from typing import Any, List, Dict, Tuple
from dotenv import set_key
from app.core.config import settings
from app.core.module import ModuleManager
from app.log import logger
@@ -32,7 +29,6 @@ class ConfigCenter(_PluginBase):
# 私有属性
_enabled = False
_params = ""
_writeenv = False
settings_attributes = [
"GITHUB_TOKEN", "API_TOKEN", "TMDB_API_DOMAIN", "TMDB_IMAGE_DOMAIN", "WALLPAPER",
"RECOGNIZE_SOURCE", "SCRAP_FOLLOW_TMDB", "AUTO_DOWNLOAD_USER",
@@ -46,62 +42,40 @@ class ConfigCenter(_PluginBase):
if not config:
return
self._enabled = config.get("enabled")
self._writeenv = config.get("writeenv")
if not self._enabled:
return
# 清理插件配置,从而实现默认使用.env中的数据源
self._params = config.pop("params", "")
if "undefined" in config:
del config["undefined"]
self.update_config(config={})
# 将自定义配置存储到 __ConfigCenter__
self.update_config(plugin_id="__ConfigCenter__", config={"params": self._params})
logger.info(f"正在应用配置中心配置:{config}")
for attribute in self.settings_attributes:
setattr(settings, attribute, config.get(attribute) or getattr(settings, attribute))
# 自定义配置,以换行分隔
self._params = config.get("params") or ""
for key, value in self.__parse_params(self._params).items():
if hasattr(settings, key):
setattr(settings, key, str(value))
# 追加自定义配置中的内容
params = self.__parse_params(self._params) or {}
config.update(**params)
# 批量更新配置,并获取更新结果
update_results = settings.update_settings(config)
# 遍历更新结果
for key, (success, message) in update_results.items():
if not success:
self.__log_and_notify_error(f"配置项 '{key}' 更新失败:{message}")
elif message:
self.__log_and_notify_error(f"配置项 '{key}' 更新时出现警告:{message}")
# 重新加载模块
ModuleManager().stop()
ModuleManager().load_modules()
ModuleManager().reload()
# 如果写入app.env文件则关闭插件开关
if self._writeenv:
# 写入env文件
self.update_env(config)
# 自动关闭插件
self._enabled = False
logger.info("配置中心设置已写入app.env文件插件关闭...")
# 保存配置
config.update({"enabled": False})
self.update_config(config)
def update_env(self, config: dict):
def __log_and_notify_error(self, message):
"""
更新设置到app.env
记录错误日志并发送系统通知
"""
if not config:
return
# 避免修改原值
conf = copy.deepcopy(config)
# 自定义配置,以换行分隔
config_params = self.__parse_params(conf.get("params"))
conf.update(config_params)
# 读写app.env
env_path = settings.CONFIG_PATH / "app.env"
for key, value in conf.items():
if not key:
continue
# 如果参数不在支持列表中, 则跳过
if key not in self.settings_attributes and key not in config_params:
continue
if value is None or str(value) == "None":
value = ''
else:
value = str(value)
set_key(env_path, key, value)
logger.info("app.env文件写入完成")
self.systemmessage.put("配置中心设置已写入app.env文件插件关闭", title="配置中心")
logger.error(message)
self.systemmessage.put(message, title=self.plugin_name)
@staticmethod
def __parse_params(param_str: str) -> dict:
@@ -130,7 +104,7 @@ class ConfigCenter(_PluginBase):
return result
def get_state(self) -> bool:
return self._enabled
return True
@staticmethod
def get_command() -> List[Dict[str, Any]]:
@@ -143,9 +117,13 @@ class ConfigCenter(_PluginBase):
"""
拼装插件配置页面需要返回两块数据1、页面配置2、数据结构
"""
config = self.get_config(plugin_id="__ConfigCenter__") or {}
params_str = config.get("params") or ""
params = self.__parse_params(params_str) or {}
updated_params = {key: getattr(settings, key) for key in params if hasattr(settings, key)}
params_str = "\n".join(f"{key}={value}" for key, value in updated_params.items())
default_settings = {
"enabled": False,
"params": "",
"params": params_str
}
for attribute in self.settings_attributes:
default_settings[attribute] = getattr(settings, attribute)
@@ -156,43 +134,6 @@ class ConfigCenter(_PluginBase):
{
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
"cols": 12,
"md": 6
},
"content": [
{
"component": "VSwitch",
"props": {
"model": "enabled",
"label": "启用插件"
}
}
]
},
{
"component": "VCol",
"props": {
"cols": 12,
"md": 6
},
"content": [
{
"component": "VSwitch",
"props": {
"model": "writeenv",
"label": "写入app.env文件"
}
}
]
},
]
},
{
'component': 'VRow',
'content': [
{
"component": "VCol",
"props": {
@@ -204,7 +145,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "GITHUB_TOKEN",
"label": "Github Token"
"label": "GitHub Token",
"hint": "GitHub Token提高请求API限流阈值格式: ghp_****",
"persistent-hint": True
}
}
]
@@ -220,7 +163,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "API_TOKEN",
"label": "API密钥"
"label": "API密钥",
"hint": "用于Jellyseerr/Overseerr、媒体服务器Webhook等配置以及部分支持API_TOKEN的API请求",
"persistent-hint": True
}
}
]
@@ -236,7 +181,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "TMDB_API_DOMAIN",
"label": "TMDB API地址"
"label": "TMDB API地址",
"hint": "TMDB API地址无需修改或配置为其他中转代理服务地址确保连通性",
"persistent-hint": True
}
}
]
@@ -252,7 +199,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "TMDB_IMAGE_DOMAIN",
"label": "TheMovieDb图片服务器"
"label": "TheMovieDb图片服务器",
"hint": "TheMovieDb图片服务器无需修改或修改为其他可用地址如 static-mdb.v.geilijiasu.com",
"persistent-hint": True
}
}
]
@@ -270,9 +219,17 @@ class ConfigCenter(_PluginBase):
"model": "RECOGNIZE_SOURCE",
"label": "媒体信息识别来源",
"items": [
{"title": "TheMovieDb", "value": "themoviedb"},
{"title": "豆瓣", "value": "douban"}
]
{
"title": "TheMovieDb",
"value": "themoviedb"
},
{
"title": "豆瓣",
"value": "douban"
}
],
"hint": "媒体信息识别来源",
"persistent-hint": True
}
}
]
@@ -290,9 +247,17 @@ class ConfigCenter(_PluginBase):
"model": "SCRAP_SOURCE",
"label": "刮削元数据及图片使用的数据源",
"items": [
{"title": "TheMovieDb", "value": "themoviedb"},
{"title": "豆瓣", "value": "douban"},
]
{
"title": "TheMovieDb",
"value": "themoviedb"
},
{
"title": "豆瓣",
"value": "douban"
}
],
"hint": "刮削元数据及图片使用的数据源",
"persistent-hint": True
}
}
]
@@ -309,7 +274,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "META_CACHE_EXPIRE",
"label": "元数据缓存时间(小时)",
"placeholder": "单位:小时"
"hint": "元数据缓存过期时间0为系统默认",
"persistent-hint": True
}
}
]
@@ -327,9 +293,17 @@ class ConfigCenter(_PluginBase):
"model": "WALLPAPER",
"label": "登录首页电影海报",
"items": [
{"title": "TheMovieDb电影海报", "value": "tmdb"},
{"title": "Bing每日壁纸", "value": "bing"}
]
{
"title": "TheMovieDb电影海报",
"value": "tmdb"
},
{
"title": "Bing每日壁纸",
"value": "bing"
}
],
"hint": "登录首页电影海报",
"persistent-hint": True
}
}
]
@@ -337,8 +311,8 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
@@ -350,7 +324,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "OCR_HOST",
"label": "验证码识别服务器"
"label": "验证码识别服务器",
"hint": "验证码识别服务器地址",
"persistent-hint": True
}
}
]
@@ -366,8 +342,9 @@ class ConfigCenter(_PluginBase):
"component": "VTextField",
"props": {
"model": "GITHUB_PROXY",
"label": "Github加速服务器",
"placeholder": "https://mirror.ghproxy.com/"
"label": "GitHub加速服务器",
"hint": "GitHub加速服务器格式: https://mirror.ghproxy.com/",
"persistent-hint": True
}
}
]
@@ -384,7 +361,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "PIP_PROXY",
"label": "PIP加速服务器",
"placeholder": "https://pypi.tuna.tsinghua.edu.cn/simple"
"hint": "PIP加速服务器格式: https://pypi.tuna.tsinghua.edu.cn/simple",
"persistent-hint": True
}
}
]
@@ -401,7 +379,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "MEDIASERVER_SYNC_INTERVAL",
"label": "媒体服务器同步间隔(小时)",
"placeholder": "单位:小时"
"hint": "媒体服务器同步间隔",
"persistent-hint": True
}
}
]
@@ -409,8 +388,8 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
@@ -423,7 +402,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "DOH_DOMAINS",
"label": "DOH解析的域名",
"placeholder": "多个域名使用,分隔"
"hint": "DOH解析的域名列表多个域名使用逗号分隔",
"persistent-hint": True
}
}
]
@@ -440,7 +420,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "DOH_RESOLVERS",
"label": "DOH解析服务器",
"placeholder": "多个地址使用,分隔"
"hint": "DOH解析服务器列表多个服务器使用逗号分隔",
"persistent-hint": True
}
}
]
@@ -448,19 +429,21 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
"cols": 12,
"cols": 12
},
"content": [
{
"component": "VTextarea",
"props": {
"model": "MOVIE_RENAME_FORMAT",
"label": "电影重命名格式"
"label": "电影重命名格式",
"hint": "电影重命名格式使用Jinja2语法每行一个配置项参考https://jinja.palletsprojects.com/en/3.0.x/templates/",
"persistent-hint": True
}
}
]
@@ -468,19 +451,21 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
"cols": 12,
"cols": 12
},
"content": [
{
"component": "VTextarea",
"props": {
"model": "TV_RENAME_FORMAT",
"label": "电视剧重命名格式"
"label": "电视剧重命名格式",
"hint": "电视剧重命名格式使用Jinja2语法参考https://jinja.palletsprojects.com/en/3.0.x/templates/",
"persistent-hint": True
}
}
]
@@ -488,12 +473,12 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
"cols": 12,
"cols": 12
},
"content": [
{
@@ -501,7 +486,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "PLUGIN_MARKET",
"label": "插件市场",
"placeholder": "多个地址使用,分隔"
"hint": "插件市场仓库地址,多个地址使用逗号分隔,确保每个地址以/结尾",
"persistent-hint": True
}
}
]
@@ -509,12 +495,12 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
"cols": 12,
"cols": 12
},
"content": [
{
@@ -522,7 +508,8 @@ class ConfigCenter(_PluginBase):
"props": {
"model": "params",
"label": "自定义配置",
"placeholder": "每行一个配置项,格式:配置项=值"
"hint": "自定义配置,每行一个配置项,格式:配置项=值",
"persistent-hint": True
}
}
]
@@ -530,8 +517,8 @@ class ConfigCenter(_PluginBase):
]
},
{
'component': 'VRow',
'content': [
"component": "VRow",
"content": [
{
"component": "VCol",
"props": {
@@ -543,7 +530,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "DOWNLOAD_SUBTITLE",
"label": "自动下载站点字幕"
"label": "自动下载站点字幕",
"hint": "自动下载站点字幕(如有)",
"persistent-hint": True
}
}
]
@@ -559,7 +548,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "SCRAP_FOLLOW_TMDB",
"label": "新增入库跟随TMDB信息变化"
"label": "新增入库跟随TMDB信息变化",
"hint": "新增入库媒体是否跟随TMDB信息变化",
"persistent-hint": True
}
}
]
@@ -575,7 +566,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "FANART_ENABLE",
"label": "使用Fanart图片数据源"
"label": "使用Fanart图片数据源",
"hint": "启用Fanart图片数据源",
"persistent-hint": True
}
}
]
@@ -591,7 +584,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "DOH_ENABLE",
"label": "启用DNS over HTTPS"
"label": "启用DNS over HTTPS",
"hint": "是否启用DNS over HTTPS启用后对特定域名使用DOH解析以避免DNS污染",
"persistent-hint": True
}
}
]
@@ -607,7 +602,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "SEARCH_MULTIPLE_NAME",
"label": "资源搜索整合多名称搜索结果"
"label": "资源搜索整合多名称搜索结果",
"hint": "搜索多个名称时是否整合多名称的搜索结果True/false",
"persistent-hint": True
}
}
]
@@ -623,7 +620,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "AUXILIARY_AUTH_ENABLE",
"label": "启用用户辅助认证"
"label": "启用用户辅助认证",
"hint": "是否启用用户辅助认证,允许通过外部服务进行认证、单点登录以及自动创建用户",
"persistent-hint": True
}
}
]
@@ -639,7 +638,9 @@ class ConfigCenter(_PluginBase):
"component": "VSwitch",
"props": {
"model": "GLOBAL_IMAGE_CACHE",
"label": "全局图片缓存"
"label": "全局图片缓存",
"hint": "是否启用全局图片缓存,将媒体图片缓存到本地",
"persistent-hint": True
}
}
]
@@ -658,9 +659,9 @@ class ConfigCenter(_PluginBase):
{
'component': 'VAlert',
'props': {
'type': 'info',
'type': 'warning',
'variant': 'tonal',
'text': '注意:开启写入app.env后将直接修改配置文件否则只是运行时修改生效对应配置插件关闭且重启后配置失效有些自定义配置需要重启才能生效'
'text': '注意:部分配置项的更改可能需要重启服务才能生效,为确保配置一致性,已在环境变量中的相关配置项,请手动更新'
}
}
]