Merge pull request #2811 from InfinityPacer/feature/module

This commit is contained in:
jxxghp
2024-10-06 11:08:40 +08:00
committed by GitHub
4 changed files with 109 additions and 47 deletions
+29 -24
View File
@@ -4,7 +4,6 @@ from datetime import datetime
from typing import Union, Any from typing import Union, Any
import tailer import tailer
from dotenv import set_key
from fastapi import APIRouter, HTTPException, Depends, Response from fastapi import APIRouter, HTTPException, Depends, Response
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
@@ -23,6 +22,7 @@ from app.helper.rule import RuleHelper
from app.helper.sites import SitesHelper from app.helper.sites import SitesHelper
from app.monitor import Monitor from app.monitor import Monitor
from app.scheduler import Scheduler from app.scheduler import Scheduler
from app.schemas.types import SystemConfigKey
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
from app.utils.system import SystemUtils from app.utils.system import SystemUtils
from version import APP_VERSION from version import APP_VERSION
@@ -112,19 +112,28 @@ def set_env_setting(env: dict,
""" """
更新系统环境变量(仅管理员) 更新系统环境变量(仅管理员)
""" """
for k, v in env.items(): result = settings.update_settings(env=env)
if k == "undefined": # 统计成功和失败的结果
continue success_updates = {k: v for k, v in result.items() if v[0]}
if hasattr(settings, k): failed_updates = {k: v for k, v in result.items() if not v[0]}
if v == "None":
v = None if failed_updates:
setattr(settings, k, v) return schemas.Response(
if v is None: success=False,
v = '' message="部分配置项更新失败",
else: data={
v = str(v) "success_updates": success_updates,
set_key(SystemUtils.get_env_path(), k, v) "failed_updates": failed_updates
return schemas.Response(success=True) }
)
return schemas.Response(
success=True,
message="所有配置项更新成功",
data={
"success_updates": success_updates
}
)
@router.get("/progress/{process_type}", summary="实时进度") @router.get("/progress/{process_type}", summary="实时进度")
@@ -173,17 +182,13 @@ def set_setting(key: str, value: Union[list, dict, bool, int, str] = None,
更新系统设置(仅管理员) 更新系统设置(仅管理员)
""" """
if hasattr(settings, key): if hasattr(settings, key):
if value == "None": success, message = settings.update_setting(key=key, value=value)
value = None return schemas.Response(success=success, message=message)
setattr(settings, key, value) elif key in {item.value for item in SystemConfigKey}:
if value is None:
value = ''
else:
value = str(value)
set_key(SystemUtils.get_env_path(), key, value)
else:
SystemConfigOper().set(key, value) SystemConfigOper().set(key, value)
return schemas.Response(success=True) return schemas.Response(success=True)
else:
return schemas.Response(success=False, message=f"配置项 '{key}' 不存在")
@router.get("/message", summary="实时消息") @router.get("/message", summary="实时消息")
+2 -2
View File
@@ -1118,9 +1118,9 @@ class SubscribeChain(ChainBase):
""" """
default_subscribe_key = None default_subscribe_key = None
if mtype == MediaType.TV: if mtype == MediaType.TV:
default_subscribe_key = "DefaultTvSubscribeConfig" default_subscribe_key = SystemConfigKey.DefaultTvSubscribeConfig.value
if mtype == MediaType.MOVIE: if mtype == MediaType.MOVIE:
default_subscribe_key = "DefaultMovieSubscribeConfig" default_subscribe_key = SystemConfigKey.DefaultMovieSubscribeConfig.value
# 默认订阅规则 # 默认订阅规则
if hasattr(settings, default_subscribe_key): if hasattr(settings, default_subscribe_key):
+74 -21
View File
@@ -1,9 +1,10 @@
import os import os
import re
import secrets import secrets
import sys import sys
import threading import threading
from pathlib import Path from pathlib import Path
from typing import Optional, List, Any, Type, Tuple from typing import Optional, List, Any, Type, Tuple, Dict
from dotenv import set_key from dotenv import set_key
from pydantic import BaseSettings, validator, BaseModel from pydantic import BaseSettings, validator, BaseModel
@@ -217,24 +218,26 @@ class Settings(BaseSettings, ConfigModel):
SystemUtils.copy(self.INNER_CONFIG_PATH / "app.env", app_env_path) SystemUtils.copy(self.INNER_CONFIG_PATH / "app.env", app_env_path)
@validator("API_TOKEN", pre=True, always=True) @validator("API_TOKEN", pre=True, always=True)
def validate_api_token(cls, v): def validate_api_token(cls, value: Any, field):
if not v: if not value or len(value) < 16:
new_token = secrets.token_urlsafe(16) new_token = secrets.token_urlsafe(16)
logger.info(f"'API_TOKEN' 未设置,已随机生成新的 API_TOKEN:{new_token}") if not value:
set_key(str(SystemUtils.get_env_path()), "API_TOKEN", new_token) logger.info(f"'API_TOKEN' 未设置,已随机生成新的 API_TOKEN:{new_token}")
else:
logger.warning(f"'API_TOKEN' 长度不足 16 个字符,存在安全隐患,已生成新的更复杂的 API_TOKEN:{new_token}")
cls.update_env_config(field, original_value=value or "", converted_value=new_token)
return new_token return new_token
elif len(v) < 16: return value
logger.warning("'API_TOKEN' 长度不足 16 个字符,存在安全隐患,建议尽快更换为更复杂的密钥!")
return v
@staticmethod @staticmethod
def generic_type_converter(value: Any, expected_type: Type, default: Any, field_name: str) -> Tuple[Any, bool]: def generic_type_converter(value: Any, original_value: Any, expected_type: Type, default: Any, field_name: str,
raise_exception: bool = False) -> Tuple[Any, bool]:
"""
通用类型转换函数,根据预期类型转换值。如果转换失败,返回默认值
""" """
通用类型转换函数,根据预期类型转换值。如果转换失败,返回默认值 """
if value is None: if value is None:
return default, False return default, False
original_value = value
if isinstance(value, str): if isinstance(value, str):
value = value.strip() value = value.strip()
@@ -266,6 +269,10 @@ class Settings(BaseSettings, ConfigModel):
converted = float(value) converted = float(value)
return converted, value != original_value return converted, value != original_value
elif expected_type is str: elif expected_type is str:
# 清理 value 中所有空白字符的字段
fields_not_keep_spaces = {"AUTO_DOWNLOAD_USER", "REPO_GITHUB_TOKEN", "PLUGIN_MARKET"}
if field_name in fields_not_keep_spaces:
value = re.sub(r"\s+", "", value)
return value, value != original_value return value, value != original_value
# # 后续考虑支持 list 类型的处理 # # 后续考虑支持 list 类型的处理
# elif expected_type is list: # elif expected_type is list:
@@ -277,25 +284,71 @@ class Settings(BaseSettings, ConfigModel):
# 可根据需要添加更多类型处理 # 可根据需要添加更多类型处理
else: else:
return value, False return value, False
except (ValueError, TypeError): except (ValueError, TypeError) as e:
if raise_exception:
raise ValueError(f"配置项 '{field_name}' 的值 '{value}' 无法转换成正确的类型") from e
logger.error(
f"配置项 '{field_name}' 的值 '{value}' 无法转换成正确的类型,使用默认值 '{default}',错误信息: {e}")
return default, True return default, True
@validator('*', pre=True, always=True) @validator('*', pre=True, always=True)
def generic_type_validator(cls, value: Any, field): def generic_type_validator(cls, value: Any, field):
""" """
通用校验器 通用校验器,尝试将配置值转换为期望的类型
""" """
converted_value, needs_update = cls.generic_type_converter(value, field.type_, field.default, field.name) converted_value, needs_update = cls.generic_type_converter(value, value, field.type_, field.default,
field.name)
if needs_update: if needs_update:
logger.error(f"字段 '{field.name}' 的值 '{value}' 无效,已使用 '{converted_value}' 进行替换") cls.update_env_config(field, value, converted_value)
if field.name in os.environ:
logger.warning(f"字段 '{field.name}' 已存在于环境变量中,请手动修改")
else:
set_key(SystemUtils.get_env_path(), field.name,
str(converted_value) if converted_value is not None else "")
logger.info(f"字段 '{field.name}' 已由应用修改并写入到 app.env 中")
return converted_value return converted_value
@staticmethod
def update_env_config(field: Any, original_value: Any, converted_value: Any) -> Tuple[bool, str]:
"""
更新 env 配置
"""
is_converted = original_value is not None and original_value != converted_value
if is_converted:
logger.warning(f"配置项 '{field.name}' 的值 '{original_value}' 无效,已替换为 '{converted_value}'")
if field.name in os.environ:
if is_converted:
message = f"配置项 '{field.name}' 已在环境变量中设置,请手动更新以保持一致性"
logger.warning(message)
return False, message
return True, ""
else:
set_key(SystemUtils.get_env_path(), field.name, str(converted_value) if converted_value is not None else "")
if is_converted:
logger.info(f"配置项 '{field.name}' 已自动修正并写入到 'app.env' 文件")
return True, ""
def update_setting(self, key: str, value: Any) -> Tuple[bool, str]:
"""
更新单个配置项
"""
if not hasattr(self, key):
return False, f"配置项 '{key}' 不存在"
try:
field = self.__fields__[key]
converted_value, _ = self.generic_type_converter(value, getattr(self, key), field.type_,
field.default, key)
# 如果没有抛出异常,则统一使用 converted_value 进行更新
setattr(self, key, converted_value)
return self.update_env_config(field, value, converted_value)
except Exception as e:
return False, str(e)
def update_settings(self, env: Dict[str, Any]) -> Dict[str, Tuple[bool, str]]:
"""
更新多个配置项
"""
results = {}
for k, v in env.items():
results[k] = self.update_setting(k, v)
return results
@property @property
def VERSION_FLAG(self) -> str: def VERSION_FLAG(self) -> str:
""" """
+4
View File
@@ -113,6 +113,10 @@ class SystemConfigKey(Enum):
UserInstalledPlugins = "UserInstalledPlugins" UserInstalledPlugins = "UserInstalledPlugins"
# 插件安装统计 # 插件安装统计
PluginInstallReport = "PluginInstallReport" PluginInstallReport = "PluginInstallReport"
# 默认电影订阅规则
DefaultMovieSubscribeConfig = "DefaultMovieSubscribeConfig"
# 默认电视剧订阅规则
DefaultTvSubscribeConfig = "DefaultTvSubscribeConfig"
# 处理进度Key字典 # 处理进度Key字典