fix: 配置 API 掩码方案兼容管理 UI

- GET /api/v2/config 返回完整配置的 JSON 字符串(敏感字段掩码为 ********),UI 编辑器可用
- PUT 保存时剔除掩码字段,防止掩码写回覆盖真实密钥(deep_merge 只更新真实改动)
This commit is contained in:
HYC Fixer
2026-09-02 00:41:43 +08:00
parent ccd66b806f
commit e5c494f64b
+38 -14
View File
@@ -3493,8 +3493,39 @@ class APIv2(APIv1):
print(trace) print(trace)
handler.send_json_response({"error": str(e)}, 500) 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): def api_get_config(self, handler):
"""获取配置文件内容 (settings.json)""" """获取配置文件内容 (settings.json, 敏感字段掩码化, 返回 JSON 字符串兼容 UI)"""
try: try:
import os import os
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -3512,28 +3543,18 @@ class APIv2(APIv1):
}, 404) }, 404)
return return
# 脱敏: 不返回 settings.json 原文(含 auth_pass/auth_token 等密钥),
# 只返回精选非敏感字段
try: try:
with open(config_path, 'r', encoding='utf-8') as f: with open(config_path, 'r', encoding='utf-8') as f:
raw = json.load(f) raw = json.load(f)
except Exception: except Exception:
raw = {} raw = {}
safe_keys = [ masked = self._mask_config(raw)
'server_name', 'host', 'port', 'base_dir', 'api_version', config_str = json.dumps(masked, ensure_ascii=False, indent=2)
'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}
handler.send_json_response({ handler.send_json_response({
"success": True, "success": True,
"config": safe_config, "config": config_str,
"path": config_path, "path": config_path,
"filename": os.path.basename(config_path) "filename": os.path.basename(config_path)
}) })
@@ -3574,6 +3595,9 @@ class APIv2(APIv1):
}, 400) }, 400)
return return
# 剔除掩码字段(值为 ******** 表示未修改, 防止覆盖真实密钥)
updates = self._unmask_config(updates)
# 读取现有配置 # 读取现有配置
import os import os
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))