From e5c494f64b55477b64462689bc180d5a37c384f0 Mon Sep 17 00:00:00 2001 From: HYC Fixer Date: Wed, 2 Sep 2026 00:41:43 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=85=8D=E7=BD=AE=20API=20=E6=8E=A9?= =?UTF-8?q?=E7=A0=81=E6=96=B9=E6=A1=88=E5=85=BC=E5=AE=B9=E7=AE=A1=E7=90=86?= =?UTF-8?q?=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /api/v2/config 返回完整配置的 JSON 字符串(敏感字段掩码为 ********),UI 编辑器可用 - PUT 保存时剔除掩码字段,防止掩码写回覆盖真实密钥(deep_merge 只更新真实改动) --- api/v2.py | 52 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/api/v2.py b/api/v2.py index 06e042d..c636fcc 100644 --- a/api/v2.py +++ b/api/v2.py @@ -3493,8 +3493,39 @@ class APIv2(APIv1): print(trace) handler.send_json_response({"error": str(e)}, 500) + # 配置脱敏: 敏感字段以掩码显示, 保存时剔除掩码值避免覆盖真实密钥 + _CONFIG_MASK = '********' + _CONFIG_SENSITIVE_KEYS = { + 'auth_pass', 'auth_token', 'auth_secret', 'smtp_password', + 'password', 'secret_key', 'access_key', 'private_key', + } + + @classmethod + def _mask_config(cls, data): + """递归掩码敏感字段(保留 JSON 结构,供 UI 编辑非敏感字段)""" + if isinstance(data, dict): + return {k: (cls._CONFIG_MASK if k in cls._CONFIG_SENSITIVE_KEYS and v else cls._mask_config(v)) + for k, v in data.items()} + if isinstance(data, list): + return [cls._mask_config(v) for v in data] + return data + + @classmethod + def _unmask_config(cls, data): + """递归剔除值为掩码的敏感字段(防止掩码写回覆盖真实值)""" + if isinstance(data, dict): + result = {} + for k, v in data.items(): + if k in cls._CONFIG_SENSITIVE_KEYS and v == cls._CONFIG_MASK: + continue # 掩码值 = 未修改,保留服务器现有值 + result[k] = cls._unmask_config(v) + return result + if isinstance(data, list): + return [cls._unmask_config(v) for v in data] + return data + def api_get_config(self, handler): - """获取配置文件内容 (settings.json)""" + """获取配置文件内容 (settings.json, 敏感字段掩码化, 返回 JSON 字符串兼容 UI)""" try: import os project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -3512,28 +3543,18 @@ class APIv2(APIv1): }, 404) return - # 脱敏: 不返回 settings.json 原文(含 auth_pass/auth_token 等密钥), - # 只返回精选非敏感字段 try: with open(config_path, 'r', encoding='utf-8') as f: raw = json.load(f) except Exception: raw = {} - safe_keys = [ - 'server_name', 'host', 'port', 'base_dir', 'api_version', - 'directory_listing', 'enable_stats', 'show_hash', 'ignore_hidden', - 'enable_range', 'max_workers', 'timeout', 'max_upload_size', - 'enable_ws', 'enable_sse', 'enable_monitor', 'monitor_interval', - 'enable_sync', 'enable_mirrors', 'auth_type', 'log_level', - 'cache_size', 'cache_ttl', 'sort_by', 'sort_reverse', - 'max_search_results', 'session_timeout', 'sync_interval', - ] - safe_config = {k: raw.get(k) for k in safe_keys if k in raw} + masked = self._mask_config(raw) + config_str = json.dumps(masked, ensure_ascii=False, indent=2) handler.send_json_response({ "success": True, - "config": safe_config, + "config": config_str, "path": config_path, "filename": os.path.basename(config_path) }) @@ -3574,6 +3595,9 @@ class APIv2(APIv1): }, 400) return + # 剔除掩码字段(值为 ******** 表示未修改, 防止覆盖真实密钥) + updates = self._unmask_config(updates) + # 读取现有配置 import os project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))