From 8b336cf3eb306711b82edb133fe449557015ea5d Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:44:35 +0800 Subject: [PATCH 1/4] fix(log): add support for CONFIG_DIR through environment variables --- app/log.py | 10 ++++++++-- app/utils/system.py | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/log.py b/app/log.py index 008a1eda..b93c795b 100644 --- a/app/log.py +++ b/app/log.py @@ -2,7 +2,7 @@ import inspect import logging from logging.handlers import RotatingFileHandler from pathlib import Path -from typing import Dict, Any +from typing import Dict, Any, Optional import click from pydantic import BaseSettings @@ -14,6 +14,8 @@ class LogSettings(BaseSettings): """ 日志设置 """ + # 配置文件目录 + CONFIG_DIR: Optional[str] = None # 是否为调试模式 DEBUG: bool = False # 日志级别(DEBUG、INFO、WARNING、ERROR等) @@ -27,12 +29,16 @@ class LogSettings(BaseSettings): # 文件日志格式 LOG_FILE_FORMAT: str = "【%(levelname)s】%(asctime)s - %(message)s" + @property + def CONFIG_PATH(self): + return SystemUtils.get_config_path(self.CONFIG_DIR) + @property def LOG_PATH(self): """ 获取日志存储路径 """ - return SystemUtils.get_config_path() / "logs" + return self.CONFIG_PATH / "logs" @property def LOG_MAX_FILE_SIZE_BYTES(self): diff --git a/app/utils/system.py b/app/utils/system.py index ae6593f9..faeb173d 100644 --- a/app/utils/system.py +++ b/app/utils/system.py @@ -5,7 +5,7 @@ import re import shutil import sys from pathlib import Path -from typing import List, Union, Tuple +from typing import List, Union, Tuple, Optional import docker import psutil @@ -473,10 +473,14 @@ class SystemUtils: return os.stat(src).st_dev == os.stat(dest).st_dev @staticmethod - def get_config_path() -> Path: + def get_config_path(config_dir: Optional[str] = None) -> Path: """ 获取配置路径 """ + if not config_dir: + config_dir = os.getenv("CONFIG_DIR") + if config_dir: + return Path(config_dir) if SystemUtils.is_docker(): return Path("/config") elif SystemUtils.is_frozen(): From 540f5eb77f427ce1a2d151ca8ccc02d224ce1063 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Thu, 12 Sep 2024 00:05:07 +0800 Subject: [PATCH 2/4] refactor: unify env path --- app/api/endpoints/system.py | 4 ++-- app/core/config.py | 29 ++++++++++++++--------------- app/log.py | 2 +- app/utils/system.py | 7 +++++++ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index 0a62b59d..e21ecb44 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -123,7 +123,7 @@ def set_env_setting(env: dict, v = '' else: v = str(v) - set_key(settings.CONFIG_PATH / "app.env", k, v) + set_key(SystemUtils.get_env_path(), k, v) return schemas.Response(success=True) @@ -180,7 +180,7 @@ def set_setting(key: str, value: Union[list, dict, bool, int, str] = None, value = '' else: value = str(value) - set_key(settings.CONFIG_PATH / "app.env", key, value) + set_key(SystemUtils.get_env_path(), key, value) else: SystemConfigOper().set(key, value) return schemas.Response(success=True) diff --git a/app/core/config.py b/app/core/config.py index 48638f72..8a6d29f8 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -117,7 +117,7 @@ class Settings(BaseSettings): '.pcm', '.rmi', '.s3m', '.snd', '.spx', '.tak', '.tta', '.vqf', '.wav', '.wma', '.aifc', '.aiff', '.alac', '.adif', '.adts', - '.flac', '.midi', '.opus', '.sfalc'] + '.flac', '.midi', '.opus', '.sfalc'] # 下载器临时文件后缀 DOWNLOAD_TMPEXT: list = ['.!qB', '.part'] # 下载器监视间隔(小时) @@ -272,16 +272,16 @@ class Settings(BaseSettings): """ if self.PROXY_HOST: parsed_url = urlparse(self.PROXY_HOST) - protocol = parsed_url.scheme or "" # 协议 - username = parsed_url.username or "" # 用户名 - password = parsed_url.password or "" # 密码 - host = parsed_url.hostname or "" # 主机 - port = parsed_url.port or "" # 端口 - path = parsed_url.path or "" # 路径 - netloc = parsed_url.netloc or "" # 用户名:密码@主机:端口 - query = parsed_url.query or "" # 查询参数: ?key=value - params = parsed_url.params or "" # 使用;分割的参数 - fragment = parsed_url.fragment or "" # 片段: #fragment + protocol = parsed_url.scheme or "" # 协议 + username = parsed_url.username or "" # 用户名 + password = parsed_url.password or "" # 密码 + host = parsed_url.hostname or "" # 主机 + port = parsed_url.port or "" # 端口 + path = parsed_url.path or "" # 路径 + netloc = parsed_url.netloc or "" # 用户名:密码@主机:端口 + query = parsed_url.query or "" # 查询参数: ?key=value + params = parsed_url.params or "" # 使用;分割的参数 + fragment = parsed_url.fragment or "" # 片段: #fragment if not port: if protocol == "https": @@ -414,6 +414,8 @@ class Settings(BaseSettings): class Config: case_sensitive = True + env_file = SystemUtils.get_env_path() + env_file_encoding = "utf-8" class GlobalVar(object): @@ -451,10 +453,7 @@ class GlobalVar(object): # 实例化配置 -settings = Settings( - _env_file=Settings().CONFIG_PATH / "app.env", - _env_file_encoding="utf-8" -) +settings = Settings() # 全局标识 global_vars = GlobalVar() diff --git a/app/log.py b/app/log.py index b93c795b..f9efea32 100644 --- a/app/log.py +++ b/app/log.py @@ -49,7 +49,7 @@ class LogSettings(BaseSettings): class Config: case_sensitive = True - env_file = SystemUtils.get_config_path() / "app.env" + env_file = SystemUtils.get_env_path() env_file_encoding = "utf-8" diff --git a/app/utils/system.py b/app/utils/system.py index faeb173d..27666e96 100644 --- a/app/utils/system.py +++ b/app/utils/system.py @@ -487,3 +487,10 @@ class SystemUtils: return Path(sys.executable).parent / "config" else: return Path(__file__).parents[2] / "config" + + @staticmethod + def get_env_path() -> Path: + """ + 获取配置路径 + """ + return SystemUtils.get_config_path() / "app.env" From 23b9774c5dcdfba53c866f1f656392181a628a53 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Thu, 12 Sep 2024 00:17:26 +0800 Subject: [PATCH 3/4] feat(auth): add API_TOKEN validation and auto-generation --- app/core/config.py | 15 ++++++++++++++- config/app.env | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index 8a6d29f8..95337157 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -5,8 +5,10 @@ from pathlib import Path from typing import Optional, List from urllib.parse import urlparse +from dotenv import set_key from pydantic import BaseSettings, validator +from app.log import logger from app.utils.system import SystemUtils @@ -61,7 +63,7 @@ class Settings(BaseSettings): # 超级管理员 SUPERUSER: str = "admin" # API密钥,需要更换 - API_TOKEN: str = "moviepilot" + API_TOKEN: Optional[str] = None # 网络代理 IP:PORT PROXY_HOST: Optional[str] = None # 登录页面电影海报,tmdb/bing @@ -198,6 +200,17 @@ class Settings(BaseSettings): except (ValueError, TypeError): raise ValueError(f"{value} 格式错误,不是有效数字!") + @validator("API_TOKEN", pre=True, always=True) + def validate_api_token(cls, v): + if not v: + new_token = secrets.token_urlsafe(16) + logger.info(f"API_TOKEN 未设置,已随机生成新的 API_TOKEN:{new_token}") + set_key(str(SystemUtils.get_env_path()), "API_TOKEN", new_token) + return new_token + elif len(v) < 16: + logger.warning("API_TOKEN 长度不足 16 个字符,存在安全隐患,建议尽快更换为更复杂的密钥!") + return v + @property def INNER_CONFIG_PATH(self): return self.ROOT_PATH / "config" diff --git a/config/app.env b/config/app.env index 6529b9c0..4c0b0d3f 100644 --- a/config/app.env +++ b/config/app.env @@ -29,8 +29,8 @@ DOH_RESOLVERS=1.0.0.1,1.1.1.1,9.9.9.9,149.112.112.112 META_CACHE_EXPIRE=0 # 自动检查和更新站点资源包(索引、认证等) AUTO_UPDATE_RESOURCE=true -# 【*】API密钥,建议更换复杂字符串,有Jellyseerr/Overseerr、媒体服务器Webhook等配置以及部分支持API_TOKEN的API中使用 -API_TOKEN=moviepilot +# 【*】API密钥,未设置时系统将随机生成,建议使用复杂字符串,用于Jellyseerr/Overseerr、媒体服务器Webhook等配置以及部分支持API_TOKEN的API请求 +API_TOKEN='' # 登录页面电影海报,tmdb/bing,tmdb要求能正常连接api.themoviedb.org WALLPAPER=tmdb # TMDB图片地址,无需修改需保留默认值,如果默认地址连通性不好可以尝试修改为:`static-mdb.v.geilijiasu.com` From 3446aec6a2635f0984313a00132c44199293c640 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Thu, 12 Sep 2024 02:36:34 +0800 Subject: [PATCH 4/4] feat(plugin): add API_TOKEN validation for plugin API registration --- app/api/endpoints/plugin.py | 6 +++++- app/core/plugin.py | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 52077d14..e79c3452 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, Header from app import schemas from app.core.plugin import PluginManager -from app.core.security import verify_token +from app.core.security import verify_token, verify_apitoken from app.db.systemconfig_oper import SystemConfigOper from app.db.user_oper import get_current_active_superuser from app.helper.plugin import PluginHelper @@ -23,6 +23,10 @@ def register_plugin_api(plugin_id: str = None): if r.path == api.get("path"): router.routes.remove(r) break + # 检查是否允许匿名访问,如果不允许匿名访问,则添加 API_TOKEN 验证 + allow_anonymous = api.pop("allow_anonymous", False) + if not allow_anonymous: + api.setdefault("dependencies", []).append(Depends(verify_apitoken)) router.add_api_route(**api) diff --git a/app/core/plugin.py b/app/core/plugin.py index 3cf9eb13..6b977774 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -426,7 +426,8 @@ class PluginManager(metaclass=Singleton): "endpoint": self.xxx, "methods": ["GET", "POST"], "summary": "API名称", - "description": "API说明" + "description": "API说明", + "allow_anonymous": false }] """ ret_apis = []