mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor(config): retire RuntimeSettingsCompat host usage
This commit is contained in:
+46
-49
@@ -20,13 +20,10 @@ import psutil
|
||||
|
||||
from app.adapters.system.backup.database import verify_database_backup
|
||||
from app.adapters.system.backup.files import BackupFiles
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.topology import process_topology_issue
|
||||
from app.doctor.models import DoctorFinding, DoctorFindingStatus, DoctorReport, DoctorSeverity
|
||||
from app.adapters.system.host import SystemUtils
|
||||
|
||||
from app.doctor.models import DoctorFinding, DoctorFindingStatus, DoctorReport, DoctorSeverity
|
||||
from app.runtime.settings import get_runtime_setting, update_runtime_setting
|
||||
from app.runtime.topology import process_topology_issue
|
||||
|
||||
CheckFunc = Callable[["DoctorRunnerProtocol"], None]
|
||||
|
||||
@@ -72,23 +69,23 @@ SENSITIVE_PATTERNS = (
|
||||
|
||||
|
||||
def _backend_runtime_file() -> Path:
|
||||
return settings.TEMP_PATH / "moviepilot.runtime.json"
|
||||
return get_runtime_setting('TEMP_PATH') / "moviepilot.runtime.json"
|
||||
|
||||
|
||||
def _frontend_runtime_file() -> Path:
|
||||
return settings.TEMP_PATH / "moviepilot.frontend.runtime.json"
|
||||
return get_runtime_setting('TEMP_PATH') / "moviepilot.frontend.runtime.json"
|
||||
|
||||
|
||||
def _backend_stdio_log_file() -> Path:
|
||||
return settings.LOG_PATH / "moviepilot.stdout.log"
|
||||
return get_runtime_setting('LOG_PATH') / "moviepilot.stdout.log"
|
||||
|
||||
|
||||
def _backend_app_log_file() -> Path:
|
||||
return settings.LOG_PATH / "moviepilot.log"
|
||||
return get_runtime_setting('LOG_PATH') / "moviepilot.log"
|
||||
|
||||
|
||||
def _frontend_stdio_log_file() -> Path:
|
||||
return settings.LOG_PATH / "moviepilot.frontend.stdout.log"
|
||||
return get_runtime_setting('LOG_PATH') / "moviepilot.frontend.stdout.log"
|
||||
|
||||
|
||||
class DoctorRunnerProtocol:
|
||||
@@ -163,12 +160,12 @@ def _mask_text(text: str) -> str:
|
||||
def _check_process_topology(runner: DoctorRunnerProtocol) -> None:
|
||||
"""诊断 API worker 配置是否会复制全功能控制面。"""
|
||||
issue = process_topology_issue(
|
||||
workers=settings.API_WORKERS,
|
||||
safe_mode=settings.MOVIEPILOT_SAFE_MODE,
|
||||
workers=get_runtime_setting('API_WORKERS'),
|
||||
safe_mode=get_runtime_setting('MOVIEPILOT_SAFE_MODE'),
|
||||
)
|
||||
context = {
|
||||
"api_workers": settings.API_WORKERS,
|
||||
"safe_mode": settings.MOVIEPILOT_SAFE_MODE,
|
||||
"api_workers": get_runtime_setting('API_WORKERS'),
|
||||
"safe_mode": get_runtime_setting('MOVIEPILOT_SAFE_MODE'),
|
||||
}
|
||||
if issue:
|
||||
runner.add(
|
||||
@@ -181,7 +178,7 @@ def _check_process_topology(runner: DoctorRunnerProtocol) -> None:
|
||||
context=context,
|
||||
)
|
||||
return
|
||||
if settings.API_WORKERS != 1:
|
||||
if get_runtime_setting('API_WORKERS') != 1:
|
||||
runner.add(
|
||||
finding_id="startup.process_topology",
|
||||
severity=DoctorSeverity.Warn,
|
||||
@@ -319,7 +316,7 @@ def _backend_health_payload(port: int, timeout: float = BACKEND_HEALTH_TIMEOUT)
|
||||
读取本机后端健康接口响应,用于识别非 CLI 管理的 MoviePilot 进程。
|
||||
"""
|
||||
query = urlencode({"token": BACKEND_HEALTH_TOKEN})
|
||||
url = f"http://{_client_host(settings.HOST)}:{port}{BACKEND_HEALTH_PATH}?{query}"
|
||||
url = f"http://{_client_host(get_runtime_setting('HOST'))}:{port}{BACKEND_HEALTH_PATH}?{query}"
|
||||
request = Request(url=url, headers={"Accept": "application/json"}, method="GET")
|
||||
try:
|
||||
with urlopen(request, timeout=timeout) as response:
|
||||
@@ -465,13 +462,13 @@ def _partition_error_lines(
|
||||
|
||||
|
||||
def _frontend_dir() -> Path:
|
||||
root_public = settings.ROOT_PATH / "public"
|
||||
configured = Path(settings.FRONTEND_PATH)
|
||||
root_public = get_runtime_setting('ROOT_PATH') / "public"
|
||||
configured = Path(get_runtime_setting('FRONTEND_PATH'))
|
||||
if root_public.exists():
|
||||
return root_public
|
||||
if configured.is_absolute():
|
||||
return configured
|
||||
return settings.ROOT_PATH / configured
|
||||
return get_runtime_setting('ROOT_PATH') / configured
|
||||
|
||||
|
||||
def _unlink_if_requested(runner: DoctorRunnerProtocol, path: Path) -> bool:
|
||||
@@ -491,26 +488,26 @@ def _check_runtime_paths(runner: DoctorRunnerProtocol) -> None:
|
||||
status=DoctorFindingStatus.Ok,
|
||||
title="运行路径已识别",
|
||||
detail=(
|
||||
f"程序目录:{settings.ROOT_PATH};配置目录:{settings.CONFIG_PATH};"
|
||||
f"日志目录:{settings.LOG_PATH};Python:{sys.executable}"
|
||||
f"程序目录:{get_runtime_setting('ROOT_PATH')};配置目录:{get_runtime_setting('CONFIG_PATH')};"
|
||||
f"日志目录:{get_runtime_setting('LOG_PATH')};Python:{sys.executable}"
|
||||
),
|
||||
recommendation="如需切换配置目录,请使用 CONFIG_DIR 或本地 CLI 的 --config-dir 参数。",
|
||||
context={
|
||||
"root_path": str(settings.ROOT_PATH),
|
||||
"config_path": str(settings.CONFIG_PATH),
|
||||
"log_path": str(settings.LOG_PATH),
|
||||
"root_path": str(get_runtime_setting('ROOT_PATH')),
|
||||
"config_path": str(get_runtime_setting('CONFIG_PATH')),
|
||||
"log_path": str(get_runtime_setting('LOG_PATH')),
|
||||
"python": sys.executable,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _check_config(runner: DoctorRunnerProtocol) -> None:
|
||||
token = (settings.API_TOKEN or "").strip()
|
||||
token = (get_runtime_setting('API_TOKEN') or "").strip()
|
||||
if len(token) < 16:
|
||||
fixed = False
|
||||
detail = "API_TOKEN 未设置或长度小于 16 个字符,后端鉴权和本地工具调用可能不可用。"
|
||||
if runner.fix and "API_TOKEN" not in os.environ:
|
||||
result, message = settings.update_setting("API_TOKEN", token)
|
||||
result, message = update_runtime_setting("API_TOKEN", token)
|
||||
fixed = result is True
|
||||
if message:
|
||||
detail = f"{detail} {message}"
|
||||
@@ -537,13 +534,13 @@ def _check_config(runner: DoctorRunnerProtocol) -> None:
|
||||
recommendation="无需处理。",
|
||||
)
|
||||
|
||||
if settings.PORT == settings.NGINX_PORT:
|
||||
if get_runtime_setting('PORT') == get_runtime_setting('NGINX_PORT'):
|
||||
runner.add(
|
||||
finding_id="config.port_same",
|
||||
severity=DoctorSeverity.Error,
|
||||
status=DoctorFindingStatus.Failed,
|
||||
title="前后端端口冲突",
|
||||
detail=f"PORT 与 NGINX_PORT 都设置为 {settings.PORT}。",
|
||||
detail=f"PORT 与 NGINX_PORT 都设置为 {get_runtime_setting('PORT')}。",
|
||||
recommendation="将 PORT 或 NGINX_PORT 调整为不同端口后重启服务。",
|
||||
)
|
||||
else:
|
||||
@@ -552,11 +549,11 @@ def _check_config(runner: DoctorRunnerProtocol) -> None:
|
||||
severity=DoctorSeverity.Info,
|
||||
status=DoctorFindingStatus.Ok,
|
||||
title="前后端端口配置不同",
|
||||
detail=f"后端端口 PORT={settings.PORT};前端端口 NGINX_PORT={settings.NGINX_PORT}。",
|
||||
detail=f"后端端口 PORT={get_runtime_setting('PORT')};前端端口 NGINX_PORT={get_runtime_setting('NGINX_PORT')}。",
|
||||
recommendation="无需处理。",
|
||||
)
|
||||
|
||||
proxy_host = (settings.PROXY_HOST or "").strip()
|
||||
proxy_host = (get_runtime_setting('PROXY_HOST') or "").strip()
|
||||
if proxy_host and not re.match(r"^(https?|socks5h?)://", proxy_host, re.IGNORECASE):
|
||||
runner.add(
|
||||
finding_id="config.proxy_format",
|
||||
@@ -683,16 +680,16 @@ def _check_processes_and_ports(runner: DoctorRunnerProtocol) -> None:
|
||||
runner,
|
||||
name="backend",
|
||||
path=_backend_runtime_file(),
|
||||
port=int(settings.PORT),
|
||||
port=int(get_runtime_setting('PORT')),
|
||||
)
|
||||
frontend_process = _check_runtime_file(
|
||||
runner,
|
||||
name="frontend",
|
||||
path=_frontend_runtime_file(),
|
||||
port=int(settings.NGINX_PORT),
|
||||
port=int(get_runtime_setting('NGINX_PORT')),
|
||||
)
|
||||
_check_port(runner, name="backend", port=int(settings.PORT), managed_process=backend_process)
|
||||
_check_port(runner, name="frontend", port=int(settings.NGINX_PORT), managed_process=frontend_process)
|
||||
_check_port(runner, name="backend", port=int(get_runtime_setting('PORT')), managed_process=backend_process)
|
||||
_check_port(runner, name="frontend", port=int(get_runtime_setting('NGINX_PORT')), managed_process=frontend_process)
|
||||
|
||||
|
||||
def _check_dependencies(runner: DoctorRunnerProtocol) -> None:
|
||||
@@ -719,7 +716,7 @@ def _check_dependencies(runner: DoctorRunnerProtocol) -> None:
|
||||
|
||||
|
||||
def _check_sqlite_database(runner: DoctorRunnerProtocol) -> None:
|
||||
db_file = settings.CONFIG_PATH / "user.db"
|
||||
db_file = get_runtime_setting('CONFIG_PATH') / "user.db"
|
||||
if not db_file.exists():
|
||||
runner.add(
|
||||
finding_id="database.sqlite_missing",
|
||||
@@ -775,7 +772,7 @@ def _check_sqlite_database(runner: DoctorRunnerProtocol) -> None:
|
||||
def _check_postgresql_database(runner: DoctorRunnerProtocol) -> None:
|
||||
missing = []
|
||||
for key in ("DB_POSTGRESQL_HOST", "DB_POSTGRESQL_DATABASE", "DB_POSTGRESQL_USERNAME"):
|
||||
if not str(getattr(settings, key, "") or "").strip():
|
||||
if not str(get_runtime_setting(key, "") or "").strip():
|
||||
missing.append(key)
|
||||
if missing:
|
||||
runner.add(
|
||||
@@ -800,9 +797,9 @@ def _check_postgresql_database(runner: DoctorRunnerProtocol) -> None:
|
||||
)
|
||||
return
|
||||
|
||||
host = settings.DB_POSTGRESQL_HOST
|
||||
port = settings.DB_POSTGRESQL_PORT
|
||||
if settings.DB_POSTGRESQL_SOCKET_MODE or not port:
|
||||
host = get_runtime_setting('DB_POSTGRESQL_HOST')
|
||||
port = get_runtime_setting('DB_POSTGRESQL_PORT')
|
||||
if get_runtime_setting('DB_POSTGRESQL_SOCKET_MODE') or not port:
|
||||
runner.add(
|
||||
finding_id="database.postgresql_deep_skipped",
|
||||
severity=DoctorSeverity.Info,
|
||||
@@ -818,13 +815,13 @@ def _check_postgresql_database(runner: DoctorRunnerProtocol) -> None:
|
||||
severity=DoctorSeverity.Info if ok else DoctorSeverity.Error,
|
||||
status=DoctorFindingStatus.Ok if ok else DoctorFindingStatus.Failed,
|
||||
title="PostgreSQL TCP 端口可连接" if ok else "PostgreSQL TCP 端口不可连接",
|
||||
detail=f"{settings.DB_POSTGRESQL_TARGET} {detail}".strip(),
|
||||
detail=f"{get_runtime_setting('DB_POSTGRESQL_TARGET')} {detail}".strip(),
|
||||
recommendation="不可连接时请检查数据库服务、容器网络、端口映射和防火墙。",
|
||||
)
|
||||
|
||||
|
||||
def _check_database(runner: DoctorRunnerProtocol) -> None:
|
||||
if settings.DB_TYPE.lower() == "postgresql":
|
||||
if get_runtime_setting('DB_TYPE').lower() == "postgresql":
|
||||
_check_postgresql_database(runner)
|
||||
else:
|
||||
_check_sqlite_database(runner)
|
||||
@@ -833,9 +830,9 @@ def _check_database(runner: DoctorRunnerProtocol) -> None:
|
||||
|
||||
def _check_database_backups(runner: DoctorRunnerProtocol) -> None:
|
||||
"""列举并离线校验与当前数据库类型匹配的受管备份。"""
|
||||
db_type = "postgresql" if settings.DB_TYPE.lower() == "postgresql" else "sqlite"
|
||||
db_type = "postgresql" if get_runtime_setting('DB_TYPE').lower() == "postgresql" else "sqlite"
|
||||
try:
|
||||
paths = BackupFiles(settings.DATABASE_BACKUP_PATH).list()
|
||||
paths = BackupFiles(get_runtime_setting('DATABASE_BACKUP_PATH')).list()
|
||||
except OSError as error:
|
||||
runner.add(
|
||||
finding_id="database.backup_recovery",
|
||||
@@ -985,7 +982,7 @@ def _check_logs(runner: DoctorRunnerProtocol) -> None:
|
||||
_backend_stdio_log_file(),
|
||||
_frontend_stdio_log_file(),
|
||||
]
|
||||
plugin_log_dir = settings.LOG_PATH / "plugins"
|
||||
plugin_log_dir = get_runtime_setting('LOG_PATH') / "plugins"
|
||||
plugin_logger_names: set[str] = set()
|
||||
if plugin_log_dir.exists():
|
||||
plugin_log_files = sorted(plugin_log_dir.rglob("*.log"))
|
||||
@@ -1021,7 +1018,7 @@ def _check_logs(runner: DoctorRunnerProtocol) -> None:
|
||||
severity=DoctorSeverity.Warn,
|
||||
status=DoctorFindingStatus.Degraded,
|
||||
title="未找到运行日志",
|
||||
detail=f"{settings.LOG_PATH} 下没有可读取的 MoviePilot 日志。",
|
||||
detail=f"{get_runtime_setting('LOG_PATH')} 下没有可读取的 MoviePilot 日志。",
|
||||
recommendation="如果服务尚未启动过可忽略;否则请确认 CONFIG_DIR 和日志目录权限。",
|
||||
)
|
||||
return
|
||||
@@ -1067,7 +1064,7 @@ def _check_logs(runner: DoctorRunnerProtocol) -> None:
|
||||
status=DoctorFindingStatus.Ok,
|
||||
title="最近日志未发现明显错误关键词",
|
||||
detail=(
|
||||
f"已扫描 {settings.LOG_PATH} 下最近 {LOG_LOOKBACK_HOURS} 小时的主日志、"
|
||||
f"已扫描 {get_runtime_setting('LOG_PATH')} 下最近 {LOG_LOOKBACK_HOURS} 小时的主日志、"
|
||||
"启动日志和插件日志;插件扩展告警不参与核心健康状态。"
|
||||
),
|
||||
recommendation="如果问题仍存在,请结合具体操作时间扩大日志范围排查。",
|
||||
@@ -1113,7 +1110,7 @@ def _check_docker(runner: DoctorRunnerProtocol) -> None:
|
||||
status=DoctorFindingStatus.Ok,
|
||||
title="Docker 诊断入口可用",
|
||||
detail=(
|
||||
f"CONFIG_DIR={settings.CONFIG_PATH};VENV_PATH={os.getenv('VENV_PATH', '/opt/venv')};"
|
||||
f"CONFIG_DIR={get_runtime_setting('CONFIG_PATH')};VENV_PATH={os.getenv('VENV_PATH', '/opt/venv')};"
|
||||
f"MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE={os.getenv('MOVIEPILOT_DOCKER_KEEPALIVE_ON_FAILURE', 'true')}"
|
||||
),
|
||||
recommendation="主进程异常退出后容器会保活,仍可通过 `docker exec <container> moviepilot doctor` 诊断。",
|
||||
@@ -1121,7 +1118,7 @@ def _check_docker(runner: DoctorRunnerProtocol) -> None:
|
||||
|
||||
|
||||
def _check_safe_mode(runner: DoctorRunnerProtocol) -> None:
|
||||
if settings.MOVIEPILOT_SAFE_MODE:
|
||||
if get_runtime_setting('MOVIEPILOT_SAFE_MODE'):
|
||||
runner.add(
|
||||
finding_id="startup.safe_mode",
|
||||
severity=DoctorSeverity.Warn,
|
||||
|
||||
@@ -6,9 +6,8 @@ import sys
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from app.runtime.settings import RuntimeSettingsCompat
|
||||
from app.runtime.settings import get_runtime_setting
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.doctor.checks import default_checks
|
||||
from app.doctor.models import (
|
||||
DoctorFinding,
|
||||
@@ -110,12 +109,12 @@ class DoctorRunner:
|
||||
"platform": platform.platform(),
|
||||
"python": sys.executable,
|
||||
"python_version": platform.python_version(),
|
||||
"root_path": str(settings.ROOT_PATH),
|
||||
"config_path": str(settings.CONFIG_PATH),
|
||||
"log_path": str(settings.LOG_PATH),
|
||||
"temp_path": str(settings.TEMP_PATH),
|
||||
"root_path": str(get_runtime_setting('ROOT_PATH')),
|
||||
"config_path": str(get_runtime_setting('CONFIG_PATH')),
|
||||
"log_path": str(get_runtime_setting('LOG_PATH')),
|
||||
"temp_path": str(get_runtime_setting('TEMP_PATH')),
|
||||
"is_docker": SystemUtils.is_docker(),
|
||||
"safe_mode": settings.MOVIEPILOT_SAFE_MODE,
|
||||
"safe_mode": get_runtime_setting('MOVIEPILOT_SAFE_MODE'),
|
||||
"pid": os.getpid(),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user