mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
refactor: inject token runtime configuration
This commit is contained in:
@@ -72,6 +72,16 @@ class TransferRetryConfig:
|
|||||||
max_failed_retries: Any
|
max_failed_retries: Any
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class TokenRuntimeConfig:
|
||||||
|
"""令牌编解码在一次宿主生命周期内使用的安全配置快照。"""
|
||||||
|
|
||||||
|
secret_key: str
|
||||||
|
resource_secret_key: str
|
||||||
|
access_token_expire_minutes: int
|
||||||
|
resource_access_token_expire_seconds: int
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class ApiRuntimeConfig:
|
class ApiRuntimeConfig:
|
||||||
"""单次 API 请求使用的宿主配置快照。"""
|
"""单次 API 请求使用的宿主配置快照。"""
|
||||||
@@ -318,6 +328,7 @@ class SystemConfigService:
|
|||||||
|
|
||||||
_configured_system_config: SystemConfigService | None = None
|
_configured_system_config: SystemConfigService | None = None
|
||||||
_transfer_retry_config_provider: Callable[[], TransferRetryConfig] | None = None
|
_transfer_retry_config_provider: Callable[[], TransferRetryConfig] | None = None
|
||||||
|
_token_runtime_config_provider: Callable[[], TokenRuntimeConfig] | None = None
|
||||||
_runtime_configuration: RuntimeConfiguration | None = None
|
_runtime_configuration: RuntimeConfiguration | None = None
|
||||||
_runtime_settings_service: RuntimeSettingsService | None = None
|
_runtime_settings_service: RuntimeSettingsService | None = None
|
||||||
|
|
||||||
@@ -350,6 +361,21 @@ def get_transfer_retry_config() -> TransferRetryConfig:
|
|||||||
return _transfer_retry_config_provider()
|
return _transfer_retry_config_provider()
|
||||||
|
|
||||||
|
|
||||||
|
def configure_token_runtime_config(
|
||||||
|
provider: Callable[[], TokenRuntimeConfig],
|
||||||
|
) -> None:
|
||||||
|
"""由启动组合根登记令牌安全配置快照工厂。"""
|
||||||
|
global _token_runtime_config_provider
|
||||||
|
_token_runtime_config_provider = provider
|
||||||
|
|
||||||
|
|
||||||
|
def get_token_runtime_config() -> TokenRuntimeConfig:
|
||||||
|
"""返回当前令牌编解码使用的不可变配置快照。"""
|
||||||
|
if _token_runtime_config_provider is None:
|
||||||
|
raise RuntimeError("令牌运行时配置尚未装配")
|
||||||
|
return _token_runtime_config_provider()
|
||||||
|
|
||||||
|
|
||||||
def configure_runtime_configuration(configuration: RuntimeConfiguration) -> None:
|
def configure_runtime_configuration(configuration: RuntimeConfiguration) -> None:
|
||||||
"""由启动组合根登记各运行面使用的类型化配置快照工厂。"""
|
"""由启动组合根登记各运行面使用的类型化配置快照工厂。"""
|
||||||
global _runtime_configuration
|
global _runtime_configuration
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import base64
|
|||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
|
import importlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
@@ -16,7 +17,7 @@ from Crypto.Cipher import AES
|
|||||||
from Crypto.Util.Padding import pad
|
from Crypto.Util.Padding import pad
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
from app.runtime.config import settings
|
from app.application.configuration import TokenRuntimeConfig, get_token_runtime_config
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.token import TokenPayload
|
from app.schemas.token import TokenPayload
|
||||||
|
|
||||||
@@ -25,6 +26,22 @@ BCRYPT_ROUNDS = 12
|
|||||||
ALGORITHM = "HS256"
|
ALGORITHM = "HS256"
|
||||||
|
|
||||||
|
|
||||||
|
def _token_config() -> TokenRuntimeConfig:
|
||||||
|
"""读取启动快照;未装配时保留旧插件的独立调用兼容。"""
|
||||||
|
try:
|
||||||
|
return get_token_runtime_config()
|
||||||
|
except RuntimeError:
|
||||||
|
legacy_settings = importlib.import_module("app.runtime.config").settings
|
||||||
|
return TokenRuntimeConfig(
|
||||||
|
secret_key=legacy_settings.SECRET_KEY,
|
||||||
|
resource_secret_key=legacy_settings.RESOURCE_SECRET_KEY,
|
||||||
|
access_token_expire_minutes=legacy_settings.ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||||
|
resource_access_token_expire_seconds=(
|
||||||
|
legacy_settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PasswordTooLongError(ValueError):
|
class PasswordTooLongError(ValueError):
|
||||||
"""密码的 UTF-8 字节长度超过 bcrypt 可安全处理的上限。"""
|
"""密码的 UTF-8 字节长度超过 bcrypt 可安全处理的上限。"""
|
||||||
|
|
||||||
@@ -58,14 +75,15 @@ def create_access_token(
|
|||||||
purpose: Optional[str] = "authentication",
|
purpose: Optional[str] = "authentication",
|
||||||
) -> str:
|
) -> str:
|
||||||
"""创建带身份、权限等级和用途声明的 JWT 访问令牌。"""
|
"""创建带身份、权限等级和用途声明的 JWT 访问令牌。"""
|
||||||
|
config = _token_config()
|
||||||
if purpose == "resource":
|
if purpose == "resource":
|
||||||
default_expire = timedelta(
|
default_expire = timedelta(
|
||||||
seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS
|
seconds=config.resource_access_token_expire_seconds
|
||||||
)
|
)
|
||||||
secret_key = settings.RESOURCE_SECRET_KEY
|
secret_key = config.resource_secret_key
|
||||||
else:
|
else:
|
||||||
default_expire = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
default_expire = timedelta(minutes=config.access_token_expire_minutes)
|
||||||
secret_key = settings.SECRET_KEY
|
secret_key = config.secret_key
|
||||||
|
|
||||||
if expires_delta is not None:
|
if expires_delta is not None:
|
||||||
if expires_delta.total_seconds() <= 0:
|
if expires_delta.total_seconds() <= 0:
|
||||||
@@ -92,12 +110,13 @@ def decode_access_token(
|
|||||||
purpose: str = "authentication",
|
purpose: str = "authentication",
|
||||||
) -> TokenPayload:
|
) -> TokenPayload:
|
||||||
"""校验 JWT 签名和用途并返回框架无关的令牌载荷。"""
|
"""校验 JWT 签名和用途并返回框架无关的令牌载荷。"""
|
||||||
|
config = _token_config()
|
||||||
if not token:
|
if not token:
|
||||||
raise TokenValidationError(f"{purpose} token not found")
|
raise TokenValidationError(f"{purpose} token not found")
|
||||||
secret_key = (
|
secret_key = (
|
||||||
settings.RESOURCE_SECRET_KEY
|
config.resource_secret_key
|
||||||
if purpose == "resource"
|
if purpose == "resource"
|
||||||
else settings.SECRET_KEY
|
else config.secret_key
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, secret_key, algorithms=[ALGORITHM])
|
payload = jwt.decode(token, secret_key, algorithms=[ALGORITHM])
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from app.application.configuration import (
|
|||||||
ApiRuntimeConfig,
|
ApiRuntimeConfig,
|
||||||
ChainRuntimeConfig,
|
ChainRuntimeConfig,
|
||||||
SchedulerRuntimeConfig,
|
SchedulerRuntimeConfig,
|
||||||
|
TokenRuntimeConfig,
|
||||||
)
|
)
|
||||||
from app.runtime.config import Settings
|
from app.runtime.config import Settings
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
@@ -52,6 +53,16 @@ def build_api_runtime_config(settings: Settings) -> ApiRuntimeConfig:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_token_runtime_config(settings: Settings) -> TokenRuntimeConfig:
|
||||||
|
"""从部署设置构建令牌编解码使用的安全配置快照。"""
|
||||||
|
return TokenRuntimeConfig(
|
||||||
|
secret_key=settings.SECRET_KEY,
|
||||||
|
resource_secret_key=settings.RESOURCE_SECRET_KEY,
|
||||||
|
access_token_expire_minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||||
|
resource_access_token_expire_seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_scheduler_runtime_config(settings: Settings) -> SchedulerRuntimeConfig:
|
def build_scheduler_runtime_config(settings: Settings) -> SchedulerRuntimeConfig:
|
||||||
"""从可热更新的部署设置构建一次 Scheduler 配置快照。"""
|
"""从可热更新的部署设置构建一次 Scheduler 配置快照。"""
|
||||||
return SchedulerRuntimeConfig(
|
return SchedulerRuntimeConfig(
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ from app.application.configuration import (
|
|||||||
SystemConfigService,
|
SystemConfigService,
|
||||||
get_configured_system_config,
|
get_configured_system_config,
|
||||||
TransferRetryConfig,
|
TransferRetryConfig,
|
||||||
|
configure_token_runtime_config,
|
||||||
configure_runtime_configuration,
|
configure_runtime_configuration,
|
||||||
configure_runtime_settings,
|
configure_runtime_settings,
|
||||||
configure_system_config,
|
configure_system_config,
|
||||||
@@ -51,6 +52,7 @@ from app.startup.configuration import (
|
|||||||
build_api_runtime_config,
|
build_api_runtime_config,
|
||||||
build_chain_runtime_config,
|
build_chain_runtime_config,
|
||||||
build_scheduler_runtime_config,
|
build_scheduler_runtime_config,
|
||||||
|
build_token_runtime_config,
|
||||||
)
|
)
|
||||||
from app.application.database import configure_database_governance
|
from app.application.database import configure_database_governance
|
||||||
from app.application.service import configure_service_directory
|
from app.application.service import configure_service_directory
|
||||||
@@ -594,6 +596,7 @@ async def init_modules() -> HostRuntime:
|
|||||||
)
|
)
|
||||||
configure_runtime_configuration(host_runtime.configuration)
|
configure_runtime_configuration(host_runtime.configuration)
|
||||||
configure_runtime_settings(host_runtime.settings)
|
configure_runtime_settings(host_runtime.settings)
|
||||||
|
configure_token_runtime_config(lambda: build_token_runtime_config(settings))
|
||||||
# 先发布系统配置服务,后续启动组合步骤统一复用同一配置端口。
|
# 先发布系统配置服务,后续启动组合步骤统一复用同一配置端口。
|
||||||
configure_system_config(SystemConfigService(repository=SystemConfigOper()))
|
configure_system_config(SystemConfigService(repository=SystemConfigOper()))
|
||||||
# 旧 app.api.data 导入只保留 ABI 转发,正式 API 依赖全部读取 HostRuntime。
|
# 旧 app.api.data 导入只保留 ABI 转发,正式 API 依赖全部读取 HostRuntime。
|
||||||
|
|||||||
@@ -1016,6 +1016,7 @@ MFA/Passkey 专项测试与架构门禁通过,密钥类配置仍保留在安
|
|||||||
2026-08-23 将工作流动作 `FetchRssAction`、`ScanFileAction` 和 `AddSubscribeAction` 接入 `ChainRuntimeConfig` 快照,分别移除代理、媒体后缀和超级用户的全局 `settings` 读取;保留动作公开入口与工作流上下文行为,新增快照注入测试覆盖。配置债务由 130 个文件降至 127 个文件,宿主依赖与配置基线已更新。
|
2026-08-23 将工作流动作 `FetchRssAction`、`ScanFileAction` 和 `AddSubscribeAction` 接入 `ChainRuntimeConfig` 快照,分别移除代理、媒体后缀和超级用户的全局 `settings` 读取;保留动作公开入口与工作流上下文行为,新增快照注入测试覆盖。配置债务由 130 个文件降至 127 个文件,宿主依赖与配置基线已更新。
|
||||||
2026-08-23 将工作流动作 `FetchMediasAction` 和 `SendMessageAction` 接入 `ChainRuntimeConfig` 快照,分别移除内部 API 端口/令牌及工作流链接的全局 `settings` 读取;保留动作公开入口与消息载荷行为,新增快照注入测试覆盖。配置债务由 127 个文件降至 125 个文件,宿主依赖与配置基线已更新。
|
2026-08-23 将工作流动作 `FetchMediasAction` 和 `SendMessageAction` 接入 `ChainRuntimeConfig` 快照,分别移除内部 API 端口/令牌及工作流链接的全局 `settings` 读取;保留动作公开入口与消息载荷行为,新增快照注入测试覆盖。配置债务由 127 个文件降至 125 个文件,宿主依赖与配置基线已更新。
|
||||||
2026-08-23 将 API 路由前缀作为组合根参数传入 `init_routers`,移除路由初始化模块对全局 `settings` 的直接读取;默认参数保留旧调用兼容性,并补充自定义前缀测试。配置债务由 125 个文件降至 124 个文件。
|
2026-08-23 将 API 路由前缀作为组合根参数传入 `init_routers`,移除路由初始化模块对全局 `settings` 的直接读取;默认参数保留旧调用兼容性,并补充自定义前缀测试。配置债务由 125 个文件降至 124 个文件。
|
||||||
|
2026-08-23 将令牌编解码的密钥与过期策略接入 `TokenRuntimeConfig` 快照;启动组合根统一装配,未装配时保留 SDK/旧插件的动态回退,公开令牌函数签名不变。配置债务由 124 个文件降至 123 个文件,并补充资源/认证令牌回归测试。
|
||||||
|
|
||||||
**收口记录(2026-08-22)**:`reidentify_cache`、`nettest`、`scrape`、OpenAI `chat_completions/responses`、`get_logging` 和 Web Agent SSE 均改为稳定公开入口委托私有编排实现;四个消息交互 Handler 的公开方法也保留 ABI 并委托私有状态机。复杂度基线已清零,API/Application/Chain 入口预算、异步阻塞 ratchet 均通过;复杂度及兼容专项合计 252 项测试通过。
|
**收口记录(2026-08-22)**:`reidentify_cache`、`nettest`、`scrape`、OpenAI `chat_completions/responses`、`get_logging` 和 Web Agent SSE 均改为稳定公开入口委托私有编排实现;四个消息交互 Handler 的公开方法也保留 ABI 并委托私有状态机。复杂度基线已清零,API/Application/Chain 入口预算、异步阻塞 ratchet 均通过;复杂度及兼容专项合计 252 项测试通过。
|
||||||
随后将 `TransferChain.do_transfer` 的公开入口收口为稳定兼容 Facade,先提取媒体身份规范化阶段,保留显式
|
随后将 `TransferChain.do_transfer` 的公开入口收口为稳定兼容 Facade,先提取媒体身份规范化阶段,保留显式
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ def configure_plugin_system_services():
|
|||||||
RuntimeSettingsService,
|
RuntimeSettingsService,
|
||||||
SystemConfigService,
|
SystemConfigService,
|
||||||
TransferRetryConfig,
|
TransferRetryConfig,
|
||||||
|
configure_token_runtime_config,
|
||||||
configure_runtime_configuration,
|
configure_runtime_configuration,
|
||||||
configure_runtime_settings,
|
configure_runtime_settings,
|
||||||
configure_system_config,
|
configure_system_config,
|
||||||
@@ -44,6 +45,7 @@ def configure_plugin_system_services():
|
|||||||
build_api_runtime_config,
|
build_api_runtime_config,
|
||||||
build_chain_runtime_config,
|
build_chain_runtime_config,
|
||||||
build_scheduler_runtime_config,
|
build_scheduler_runtime_config,
|
||||||
|
build_token_runtime_config,
|
||||||
)
|
)
|
||||||
from app.application.service import configure_service_directory
|
from app.application.service import configure_service_directory
|
||||||
from app.db.session import (
|
from app.db.session import (
|
||||||
@@ -68,6 +70,7 @@ def configure_plugin_system_services():
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
configure_runtime_settings(RuntimeSettingsService(settings))
|
configure_runtime_settings(RuntimeSettingsService(settings))
|
||||||
|
configure_token_runtime_config(lambda: build_token_runtime_config(settings))
|
||||||
configure_system_config(SystemConfigService(repository=SystemConfigOper()))
|
configure_system_config(SystemConfigService(repository=SystemConfigOper()))
|
||||||
configure_transfer_retry_config(
|
configure_transfer_retry_config(
|
||||||
lambda: TransferRetryConfig(
|
lambda: TransferRetryConfig(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"root": "app"
|
"root": "app"
|
||||||
},
|
},
|
||||||
"settings_imports": {
|
"settings_imports": {
|
||||||
"count": 124,
|
"count": 123,
|
||||||
"files": [
|
"files": [
|
||||||
"app/adapters/cache/backends.py",
|
"app/adapters/cache/backends.py",
|
||||||
"app/adapters/cache/redis.py",
|
"app/adapters/cache/redis.py",
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"app/agent/tools/impl/send_voice_message.py",
|
"app/agent/tools/impl/send_voice_message.py",
|
||||||
"app/agent/tools/impl/update_agent_task.py",
|
"app/agent/tools/impl/update_agent_task.py",
|
||||||
"app/agent/tools/impl/update_system_settings.py",
|
"app/agent/tools/impl/update_system_settings.py",
|
||||||
"app/application/security/token.py",
|
|
||||||
"app/application/security/url.py",
|
"app/application/security/url.py",
|
||||||
"app/cli.py",
|
"app/cli.py",
|
||||||
"app/db/base.py",
|
"app/db/base.py",
|
||||||
|
|||||||
+4
-3
@@ -13,8 +13,8 @@
|
|||||||
"runtime_to_db": [],
|
"runtime_to_db": [],
|
||||||
"workflow_to_db": []
|
"workflow_to_db": []
|
||||||
},
|
},
|
||||||
"edge_count": 6414,
|
"edge_count": 6415,
|
||||||
"edge_sha256": "aea2c4a5f65a9800bc69a83990efd368ba0ec7a9b19e40f38f8d0df6c222ae26",
|
"edge_sha256": "d763c4fede395d6ef881e1308b68cfaf484b82ce2b6f0dec808abf4b1d820858",
|
||||||
"edges": [
|
"edges": [
|
||||||
"app -> app.runtime",
|
"app -> app.runtime",
|
||||||
"app -> app.runtime.compat",
|
"app -> app.runtime.compat",
|
||||||
@@ -2697,8 +2697,9 @@
|
|||||||
"app.application.security.passkey -> app.runtime",
|
"app.application.security.passkey -> app.runtime",
|
||||||
"app.application.security.passkey -> app.runtime.cache",
|
"app.application.security.passkey -> app.runtime.cache",
|
||||||
"app.application.security.passkey -> app.runtime.log",
|
"app.application.security.passkey -> app.runtime.log",
|
||||||
|
"app.application.security.token -> app.application",
|
||||||
|
"app.application.security.token -> app.application.configuration",
|
||||||
"app.application.security.token -> app.runtime",
|
"app.application.security.token -> app.runtime",
|
||||||
"app.application.security.token -> app.runtime.config",
|
|
||||||
"app.application.security.token -> app.runtime.log",
|
"app.application.security.token -> app.runtime.log",
|
||||||
"app.application.security.token -> app.schemas",
|
"app.application.security.token -> app.schemas",
|
||||||
"app.application.security.token -> app.schemas.token",
|
"app.application.security.token -> app.schemas.token",
|
||||||
|
|||||||
Reference in New Issue
Block a user