api-key-manager 4.1.0__tar.gz → 4.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api_key_manager-4.3.0/LICENSE +21 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/PKG-INFO +190 -10
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/README.md +187 -9
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/PKG-INFO +190 -10
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/SOURCES.txt +7 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/__init__.py +1 -1
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/api_models.py +1 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/config.py +1 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/detector.py +9 -13
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/errors.py +3 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/storage.py +57 -10
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/__init__.py +12 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/_app.py +23 -11
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/middleware.py +20 -3
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/check.py +35 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/keys.py +75 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/pyproject.toml +1 -1
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_api_endpoints.py +129 -0
- api_key_manager-4.3.0/tests/test_detector_unit.py +388 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_errors.py +1 -0
- api_key_manager-4.3.0/tests/test_routes_check.py +636 -0
- api_key_manager-4.3.0/tests/test_routes_misc.py +386 -0
- api_key_manager-4.3.0/tests/test_routes_models.py +549 -0
- api_key_manager-4.3.0/tests/test_routes_providers.py +506 -0
- api_key_manager-4.3.0/tests/test_routes_test.py +813 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_storage.py +217 -1
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_tester.py +1 -1
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/dependency_links.txt +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/entry_points.txt +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/requires.txt +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/top_level.txt +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/__main__.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/checker.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/cli.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/core.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/i18n.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/logger.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/model_capabilities.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/parser.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/__init__.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ai302.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/anthropic.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/baichuan.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/base.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cerebras.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cohere.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/config_providers.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/cstcloud.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope_coding.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/deepseek.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/dmxapi.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/doubao.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/fireworks.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/google.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/grok.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/groq.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/huggingface.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/hyperbolic.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/infini.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/infini_coding.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/kimi.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/kimi_coding.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/longcat.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mimo.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mimo_plan.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/minimax.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/minimax_plan.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/mistral.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/models_registry.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/modelscope.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/nvidia.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ocoolai.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/openai.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/opencode.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/opencode_zen.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/openrouter.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/perplexity.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/poe.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/ppio.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/replicate.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/siliconflow.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/stepfun.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/tencent_hunyuan.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/together.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/yi.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zai.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu_coding.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/proxy.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/py.typed +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/ssrf.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/tester.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/url_override.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/validator.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/progress.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/__init__.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/balance.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/misc.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/models.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/providers.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/stats.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/routes/test.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/web/utils.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/key_manager/webhook.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/setup.cfg +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_base_check.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_bug_fixes.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_checker.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_core.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_deepseek_balance.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_e2e.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_i18n.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_logger.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_openapi.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_parser.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_provider_detection.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_provider_refactoring.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_providers.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_proxy.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_security.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_token_limit_fast.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_v320_changes.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_validator.py +0 -0
- {api_key_manager-4.1.0 → api_key_manager-4.3.0}/tests/test_webhook.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Townrain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: api-key-manager
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Batch manage API keys for 45+ AI providers with CLI and Web interfaces
|
|
5
5
|
Author: Townrain
|
|
6
6
|
License: MIT
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
19
|
Requires-Python: >=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: httpx>=0.27.2
|
|
22
23
|
Requires-Dist: pyyaml>=6.0.2
|
|
23
24
|
Requires-Dist: rich>=13.9.0
|
|
@@ -32,6 +33,7 @@ Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
|
32
33
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
33
34
|
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
34
35
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
35
37
|
|
|
36
38
|
# API Key Manager
|
|
37
39
|
|
|
@@ -237,7 +239,7 @@ python web.py
|
|
|
237
239
|
|
|
238
240
|
### 加密存储
|
|
239
241
|
|
|
240
|
-
API
|
|
242
|
+
API 密钥支持 AES-256-GCM 加密存储(默认开启),每次加密使用随机盐值:
|
|
241
243
|
|
|
242
244
|
```bash
|
|
243
245
|
# 设置加密密钥(环境变量)
|
|
@@ -257,6 +259,23 @@ python web.py
|
|
|
257
259
|
}
|
|
258
260
|
```
|
|
259
261
|
|
|
262
|
+
#### 可选加密开关
|
|
263
|
+
|
|
264
|
+
从 v4.2.0 起,支持通过配置关闭加密,以明文存储密钥(便于本地开发):
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
# config.yaml
|
|
268
|
+
storage:
|
|
269
|
+
keys_file: "./data/keys.json"
|
|
270
|
+
encrypted: false # 明文存储(本地开发推荐)
|
|
271
|
+
# encrypted: true # 加密存储(生产环境推荐,默认值)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**说明:**
|
|
275
|
+
- `encrypted: true`(默认)— 使用 AES-256-GCM 加密,需设置 `KEY_MANAGER_SECRET`
|
|
276
|
+
- `encrypted: false` — 明文 JSON 存储,无需配置密钥,便于调试和迁移
|
|
277
|
+
- `load()` 自动检测文件格式,无论配置如何均可读取已有文件
|
|
278
|
+
|
|
260
279
|
### 安全防护
|
|
261
280
|
|
|
262
281
|
- **路径遍历防护** - 导入端点验证路径在允许目录内
|
|
@@ -747,6 +766,11 @@ test:
|
|
|
747
766
|
- 10
|
|
748
767
|
- 20
|
|
749
768
|
|
|
769
|
+
# 存储设置
|
|
770
|
+
storage:
|
|
771
|
+
keys_file: "./data/keys.json"
|
|
772
|
+
encrypted: true # 设为 false 可关闭加密,以明文存储(便于本地开发)
|
|
773
|
+
|
|
750
774
|
# 认证设置
|
|
751
775
|
auth:
|
|
752
776
|
api_key: "your-secret-api-key" # API 认证
|
|
@@ -840,26 +864,28 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
|
|
|
840
864
|
|
|
841
865
|
| 模块 | 测试文件 | 测试数 |
|
|
842
866
|
|------|---------|--------|
|
|
843
|
-
|
|
|
844
|
-
|
|
|
845
|
-
|
|
|
846
|
-
|
|
|
867
|
+
| 检测逻辑(单元) | `test_detector_unit.py` | 31 |
|
|
868
|
+
| 检测逻辑(端点) | `test_provider_detection.py` | — |
|
|
869
|
+
| 测试路由 | `test_routes_test.py` | 48 |
|
|
870
|
+
| 提供商路由 | `test_routes_providers.py` | 32 |
|
|
871
|
+
| 杂项路由 | `test_routes_misc.py` | 26 |
|
|
872
|
+
| 模型路由 | `test_routes_models.py` | 34 |
|
|
873
|
+
| 验证路由 | `test_routes_check.py` | 23 |
|
|
847
874
|
| 提供商合约 | `test_providers.py` | 220 |
|
|
875
|
+
| 密钥解析 | `test_parser.py` | 24 |
|
|
848
876
|
| 安全回归 | `test_security.py` | 21 |
|
|
849
877
|
| 加密存储 | `test_storage.py` | 26 |
|
|
850
878
|
| 错误系统 | `test_errors.py` | 28 |
|
|
851
879
|
| 国际化 | `test_i18n.py` | 37 |
|
|
852
|
-
| 端到端 | `test_e2e.py` | 18 |
|
|
853
880
|
| Webhook | `test_webhook.py` | 35 |
|
|
854
|
-
|
|
|
881
|
+
| 端到端 | `test_e2e.py` | 18 |
|
|
855
882
|
| 代理检测 | `test_proxy.py` | 19 |
|
|
856
883
|
| 日志系统 | `test_logger.py` | 21 |
|
|
857
|
-
| 能力测试 | `test_tester.py` | 11 |
|
|
858
884
|
| 核心门面 | `test_core.py` | 22 |
|
|
859
885
|
| Bug 修复 | `test_bug_fixes.py` | 15 |
|
|
860
886
|
| 提供商重构 | `test_provider_refactoring.py` | 32 |
|
|
861
887
|
|
|
862
|
-
**总测试数**:
|
|
888
|
+
**总测试数**: 913 | **覆盖率**: 92.09%
|
|
863
889
|
## SDK 使用
|
|
864
890
|
|
|
865
891
|
### Python SDK
|
|
@@ -946,10 +972,164 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
946
972
|
|
|
947
973
|
模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
|
|
948
974
|
|
|
975
|
+
## 预期的设计决策(安全审查说明)
|
|
976
|
+
|
|
977
|
+
> **重要**:以下设计决策是**有意为之**,不是安全漏洞。安全审查工具可能会标记这些行为,但它们是本项目作为**本地API密钥管理工具**的合理设计。
|
|
978
|
+
|
|
979
|
+
### 1. 认证默认禁用
|
|
980
|
+
|
|
981
|
+
**行为**:当未配置 `api_key` 或 `KEY_MANAGER_API_KEY` 时,所有API端点无需认证即可访问。
|
|
982
|
+
|
|
983
|
+
**原因**:这是本地开发工具,首次运行时用户可能尚未配置认证。系统会自动从加密密钥派生API token,确保在有加密密钥的情况下仍能提供基本安全。
|
|
984
|
+
|
|
985
|
+
**配置方式**:
|
|
986
|
+
```bash
|
|
987
|
+
# 方式1: 环境变量
|
|
988
|
+
set KEY_MANAGER_API_KEY=your-api-key
|
|
989
|
+
|
|
990
|
+
# 方式2: config.yaml
|
|
991
|
+
auth:
|
|
992
|
+
api_key: "your-api-key"
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
### 2. API Token注入到HTML
|
|
996
|
+
|
|
997
|
+
**行为**:`window.__API_TOKEN__` 被注入到 `templates/index.html` 的 `<head>` 中。
|
|
998
|
+
|
|
999
|
+
**原因**:前端JavaScript需要调用API,必须携带认证token。这是单页应用的标准做法,确保前端能自动携带token而无需用户手动配置。
|
|
1000
|
+
|
|
1001
|
+
**安全说明**:
|
|
1002
|
+
- Token从加密密钥派生,不是明文密码
|
|
1003
|
+
- 仅在本地访问时暴露(`localhost:18001`)
|
|
1004
|
+
- 生产环境应配置反向代理和HTTPS
|
|
1005
|
+
|
|
1006
|
+
### 3. 完整密钥检索端点
|
|
1007
|
+
|
|
1008
|
+
**行为**:`POST /api/keys/get-full-key` 返回未掩码的完整API密钥。
|
|
1009
|
+
|
|
1010
|
+
**原因**:用户需要复制完整密钥用于其他应用。这是密钥管理工具的核心功能。
|
|
1011
|
+
|
|
1012
|
+
**安全措施**:
|
|
1013
|
+
- 需要认证(Bearer token)
|
|
1014
|
+
- 通过 `key_masked` 查询,不直接暴露密钥列表
|
|
1015
|
+
- 记录审计日志
|
|
1016
|
+
|
|
1017
|
+
### 4. 速率限制存储无界增长
|
|
1018
|
+
|
|
1019
|
+
**行为**:`_RATE_LIMIT_STORE` 字典在内存中存储所有IP的请求记录,无最大条目限制。
|
|
1020
|
+
|
|
1021
|
+
**原因**:这是本地工具,通常只有少量客户端访问。DDoS攻击场景不现实(内部使用)。
|
|
1022
|
+
|
|
1023
|
+
**缓解措施**:
|
|
1024
|
+
- 每60秒清理超过5分钟不活跃的IP
|
|
1025
|
+
- 可通过 `rate_limit.requests_per_minute: 0` 禁用速率限制
|
|
1026
|
+
|
|
1027
|
+
### 5. 中间件执行顺序
|
|
1028
|
+
|
|
1029
|
+
**行为**:中间件顺序为 `rate_limit → auth → i18n`,未认证请求会消耗速率限制配额。
|
|
1030
|
+
|
|
1031
|
+
**原因**:这是内部工具,攻击者场景不现实。顺序调整对正常使用几乎无影响。
|
|
1032
|
+
|
|
1033
|
+
**说明**:即使是未认证请求,也应该被速率限制,防止意外的大量请求(如前端bug导致的循环请求)。
|
|
1034
|
+
|
|
1035
|
+
### 6. 文件不存在时返回空数据
|
|
1036
|
+
|
|
1037
|
+
**行为**:当 `keys.json` 文件不存在时,`_load_keys_data()` 返回 `{"keys": {}}` 而不是抛出异常。
|
|
1038
|
+
|
|
1039
|
+
**原因**:首次运行时,密钥文件尚未创建,这是有效状态。系统应优雅处理这种情况,而不是崩溃。
|
|
1040
|
+
|
|
1041
|
+
**实现**:在调用 `KeyStore.load()` 前检查文件是否存在:
|
|
1042
|
+
```python
|
|
1043
|
+
def _load_keys_data(config_override: dict | None = None) -> dict:
|
|
1044
|
+
cfg = config_override or config
|
|
1045
|
+
keys_path = Path(cfg["storage"]["keys_file"])
|
|
1046
|
+
if not keys_path.exists():
|
|
1047
|
+
return {"keys": {}}
|
|
1048
|
+
# ... 继原有逻辑
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
---
|
|
949
1052
|
|
|
950
1053
|
## 更新日志
|
|
951
1054
|
|
|
952
1055
|
|
|
1056
|
+
### v4.3.0 (2026-06-24)
|
|
1057
|
+
|
|
1058
|
+
- **测试覆盖率大幅提升**: 覆盖率从 79.15% 提升至 92.09%,新增 167 个测试
|
|
1059
|
+
- `detector.py`: 60% → 96% — 检测逻辑全覆盖,防止 v4.2.1 类 bug 回归
|
|
1060
|
+
- `web/routes/test.py`: 41% → 99% — 测试路由全覆盖
|
|
1061
|
+
- `web/routes/providers.py`: 37% → 100% — 提供商 CRUD 全覆盖
|
|
1062
|
+
- `web/routes/misc.py`: 53% → 100% — Webhook/日志/签名报告全覆盖
|
|
1063
|
+
- `web/routes/models.py`: 72% → 97% — 模型端点全覆盖
|
|
1064
|
+
- `web/routes/check.py`: 74% → 97% — 验证端点全覆盖
|
|
1065
|
+
|
|
1066
|
+
- **新增测试文件**:
|
|
1067
|
+
- `test_detector_unit.py` (31 tests) — 检测逻辑单元测试,覆盖 7 条核心路径
|
|
1068
|
+
- `test_routes_test.py` (48 tests) — 测试路由端点测试
|
|
1069
|
+
- `test_routes_providers.py` (32 tests) — 提供商 CRUD 路由测试
|
|
1070
|
+
- `test_routes_misc.py` (26 tests) — Webhook/日志/签名报告路由测试
|
|
1071
|
+
- `test_routes_models.py` (34 tests) — 模型列表/能力/SSE 路由测试
|
|
1072
|
+
- `test_routes_check.py` (23 tests) — 验证/SSRF/自动保存路由测试
|
|
1073
|
+
|
|
1074
|
+
- **检测逻辑回归测试**:
|
|
1075
|
+
- 锁定 `detect_provider()` 全部 7 条分支路径
|
|
1076
|
+
- 覆盖 v4.2.1 修复的两个 bug(格式匹配跳过验证、多候选直接返回)
|
|
1077
|
+
- 覆盖 402 余额不足识别、签名匹配阈值、超时处理等边界情况
|
|
1078
|
+
|
|
1079
|
+
- **Bug 修复**:
|
|
1080
|
+
- 修复 `test_tester.py` 中 `test_run_test_progress_callback` 缺失 `test_config` fixture 参数
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
### v4.2.1 (2026-06-23)
|
|
1084
|
+
|
|
1085
|
+
- **检测逻辑修复**:
|
|
1086
|
+
- 修复格式匹配跳过验证的BUG:当有多个候选者时,不再直接返回,而是继续进行并发探测验证
|
|
1087
|
+
- 修复变量命名误导:`is_valid` 重命名为 `got_models`
|
|
1088
|
+
- 清理死代码:删除不可达的500分加分逻辑
|
|
1089
|
+
|
|
1090
|
+
- **删除/复制API认证修复**:
|
|
1091
|
+
- 修复 `deleteKey()` 和 `copyKey()` 使用原生 `fetch()` 导致认证失败的问题
|
|
1092
|
+
- 改为使用 `safeFetch()` 自动携带 Authorization header
|
|
1093
|
+
|
|
1094
|
+
### v4.2.0 (2026-06-23)
|
|
1095
|
+
|
|
1096
|
+
- **统一认证系统**: 复用加密密钥作为API认证token
|
|
1097
|
+
- 新增 `derive_api_token()` 函数,从加密密钥派生API token
|
|
1098
|
+
- 使用独立的 salt(`key-manager-api-auth-token-v1`)避免与存储加密交叉
|
|
1099
|
+
- PBKDF2HMAC 600K迭代,32字节token,缓存机制
|
|
1100
|
+
- 修改 `auth_middleware()` 自动fallback到派生token
|
|
1101
|
+
- 扩展认证白名单,静态文件不需要认证
|
|
1102
|
+
|
|
1103
|
+
- **前端自动携带token**: 无需手动配置
|
|
1104
|
+
- 在 `templates/index.html` 的 `<head>` 中注入 `window.__API_TOKEN__`
|
|
1105
|
+
- 修改 `safeFetch()` 自动添加 `Authorization: Bearer <token>` 头
|
|
1106
|
+
- 修改 `models.js` 的SSE请求也携带token
|
|
1107
|
+
- 在 `state.js` 中添加 `apiToken` 字段
|
|
1108
|
+
|
|
1109
|
+
- **单个密钥删除功能**:
|
|
1110
|
+
- 新增 `POST /api/keys/delete` API端点
|
|
1111
|
+
- 前端添加删除按钮(垃圾桶图标)
|
|
1112
|
+
- 使用项目标准的 `showConfirm` 确认框
|
|
1113
|
+
|
|
1114
|
+
- **自动保存检测密钥**:
|
|
1115
|
+
- 检测密钥后自动保存到注册表
|
|
1116
|
+
- 更新密钥状态和检测记录
|
|
1117
|
+
|
|
1118
|
+
- **复制完整密钥功能**:
|
|
1119
|
+
- 新增 `POST /api/keys/get-full-key` API端点
|
|
1120
|
+
- 前端点击密钥可复制完整密钥
|
|
1121
|
+
|
|
1122
|
+
- **测试验证**:
|
|
1123
|
+
- 全量测试通过:687 passed, 1 skipped
|
|
1124
|
+
- 覆盖率:75.72%(超过 60% 要求)
|
|
1125
|
+
- 新增 5 个 `derive_api_token()` 单元测试
|
|
1126
|
+
|
|
1127
|
+
- **可选加密开关**: 支持明文存储,便于本地开发
|
|
1128
|
+
- 新增 `storage.encrypted` 配置项(默认 `true`)
|
|
1129
|
+
- 设置 `encrypted: false` 可关闭加密,以明文 JSON 存储密钥
|
|
1130
|
+
- `load()` 自动检测文件格式,兼容已有加密/明文文件
|
|
1131
|
+
- 修复 `_load_keys_data()` 解密失败时抛出异常而非返回空字典(H1 修复)
|
|
1132
|
+
|
|
953
1133
|
### v4.1.0 (2026-06-22)
|
|
954
1134
|
|
|
955
1135
|
- **Web 模块技术债务优化**:
|
|
@@ -202,7 +202,7 @@ python web.py
|
|
|
202
202
|
|
|
203
203
|
### 加密存储
|
|
204
204
|
|
|
205
|
-
API
|
|
205
|
+
API 密钥支持 AES-256-GCM 加密存储(默认开启),每次加密使用随机盐值:
|
|
206
206
|
|
|
207
207
|
```bash
|
|
208
208
|
# 设置加密密钥(环境变量)
|
|
@@ -222,6 +222,23 @@ python web.py
|
|
|
222
222
|
}
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
#### 可选加密开关
|
|
226
|
+
|
|
227
|
+
从 v4.2.0 起,支持通过配置关闭加密,以明文存储密钥(便于本地开发):
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
# config.yaml
|
|
231
|
+
storage:
|
|
232
|
+
keys_file: "./data/keys.json"
|
|
233
|
+
encrypted: false # 明文存储(本地开发推荐)
|
|
234
|
+
# encrypted: true # 加密存储(生产环境推荐,默认值)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**说明:**
|
|
238
|
+
- `encrypted: true`(默认)— 使用 AES-256-GCM 加密,需设置 `KEY_MANAGER_SECRET`
|
|
239
|
+
- `encrypted: false` — 明文 JSON 存储,无需配置密钥,便于调试和迁移
|
|
240
|
+
- `load()` 自动检测文件格式,无论配置如何均可读取已有文件
|
|
241
|
+
|
|
225
242
|
### 安全防护
|
|
226
243
|
|
|
227
244
|
- **路径遍历防护** - 导入端点验证路径在允许目录内
|
|
@@ -712,6 +729,11 @@ test:
|
|
|
712
729
|
- 10
|
|
713
730
|
- 20
|
|
714
731
|
|
|
732
|
+
# 存储设置
|
|
733
|
+
storage:
|
|
734
|
+
keys_file: "./data/keys.json"
|
|
735
|
+
encrypted: true # 设为 false 可关闭加密,以明文存储(便于本地开发)
|
|
736
|
+
|
|
715
737
|
# 认证设置
|
|
716
738
|
auth:
|
|
717
739
|
api_key: "your-secret-api-key" # API 认证
|
|
@@ -805,26 +827,28 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
|
|
|
805
827
|
|
|
806
828
|
| 模块 | 测试文件 | 测试数 |
|
|
807
829
|
|------|---------|--------|
|
|
808
|
-
|
|
|
809
|
-
|
|
|
810
|
-
|
|
|
811
|
-
|
|
|
830
|
+
| 检测逻辑(单元) | `test_detector_unit.py` | 31 |
|
|
831
|
+
| 检测逻辑(端点) | `test_provider_detection.py` | — |
|
|
832
|
+
| 测试路由 | `test_routes_test.py` | 48 |
|
|
833
|
+
| 提供商路由 | `test_routes_providers.py` | 32 |
|
|
834
|
+
| 杂项路由 | `test_routes_misc.py` | 26 |
|
|
835
|
+
| 模型路由 | `test_routes_models.py` | 34 |
|
|
836
|
+
| 验证路由 | `test_routes_check.py` | 23 |
|
|
812
837
|
| 提供商合约 | `test_providers.py` | 220 |
|
|
838
|
+
| 密钥解析 | `test_parser.py` | 24 |
|
|
813
839
|
| 安全回归 | `test_security.py` | 21 |
|
|
814
840
|
| 加密存储 | `test_storage.py` | 26 |
|
|
815
841
|
| 错误系统 | `test_errors.py` | 28 |
|
|
816
842
|
| 国际化 | `test_i18n.py` | 37 |
|
|
817
|
-
| 端到端 | `test_e2e.py` | 18 |
|
|
818
843
|
| Webhook | `test_webhook.py` | 35 |
|
|
819
|
-
|
|
|
844
|
+
| 端到端 | `test_e2e.py` | 18 |
|
|
820
845
|
| 代理检测 | `test_proxy.py` | 19 |
|
|
821
846
|
| 日志系统 | `test_logger.py` | 21 |
|
|
822
|
-
| 能力测试 | `test_tester.py` | 11 |
|
|
823
847
|
| 核心门面 | `test_core.py` | 22 |
|
|
824
848
|
| Bug 修复 | `test_bug_fixes.py` | 15 |
|
|
825
849
|
| 提供商重构 | `test_provider_refactoring.py` | 32 |
|
|
826
850
|
|
|
827
|
-
**总测试数**:
|
|
851
|
+
**总测试数**: 913 | **覆盖率**: 92.09%
|
|
828
852
|
## SDK 使用
|
|
829
853
|
|
|
830
854
|
### Python SDK
|
|
@@ -911,10 +935,164 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
911
935
|
|
|
912
936
|
模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
|
|
913
937
|
|
|
938
|
+
## 预期的设计决策(安全审查说明)
|
|
939
|
+
|
|
940
|
+
> **重要**:以下设计决策是**有意为之**,不是安全漏洞。安全审查工具可能会标记这些行为,但它们是本项目作为**本地API密钥管理工具**的合理设计。
|
|
941
|
+
|
|
942
|
+
### 1. 认证默认禁用
|
|
943
|
+
|
|
944
|
+
**行为**:当未配置 `api_key` 或 `KEY_MANAGER_API_KEY` 时,所有API端点无需认证即可访问。
|
|
945
|
+
|
|
946
|
+
**原因**:这是本地开发工具,首次运行时用户可能尚未配置认证。系统会自动从加密密钥派生API token,确保在有加密密钥的情况下仍能提供基本安全。
|
|
947
|
+
|
|
948
|
+
**配置方式**:
|
|
949
|
+
```bash
|
|
950
|
+
# 方式1: 环境变量
|
|
951
|
+
set KEY_MANAGER_API_KEY=your-api-key
|
|
952
|
+
|
|
953
|
+
# 方式2: config.yaml
|
|
954
|
+
auth:
|
|
955
|
+
api_key: "your-api-key"
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### 2. API Token注入到HTML
|
|
959
|
+
|
|
960
|
+
**行为**:`window.__API_TOKEN__` 被注入到 `templates/index.html` 的 `<head>` 中。
|
|
961
|
+
|
|
962
|
+
**原因**:前端JavaScript需要调用API,必须携带认证token。这是单页应用的标准做法,确保前端能自动携带token而无需用户手动配置。
|
|
963
|
+
|
|
964
|
+
**安全说明**:
|
|
965
|
+
- Token从加密密钥派生,不是明文密码
|
|
966
|
+
- 仅在本地访问时暴露(`localhost:18001`)
|
|
967
|
+
- 生产环境应配置反向代理和HTTPS
|
|
968
|
+
|
|
969
|
+
### 3. 完整密钥检索端点
|
|
970
|
+
|
|
971
|
+
**行为**:`POST /api/keys/get-full-key` 返回未掩码的完整API密钥。
|
|
972
|
+
|
|
973
|
+
**原因**:用户需要复制完整密钥用于其他应用。这是密钥管理工具的核心功能。
|
|
974
|
+
|
|
975
|
+
**安全措施**:
|
|
976
|
+
- 需要认证(Bearer token)
|
|
977
|
+
- 通过 `key_masked` 查询,不直接暴露密钥列表
|
|
978
|
+
- 记录审计日志
|
|
979
|
+
|
|
980
|
+
### 4. 速率限制存储无界增长
|
|
981
|
+
|
|
982
|
+
**行为**:`_RATE_LIMIT_STORE` 字典在内存中存储所有IP的请求记录,无最大条目限制。
|
|
983
|
+
|
|
984
|
+
**原因**:这是本地工具,通常只有少量客户端访问。DDoS攻击场景不现实(内部使用)。
|
|
985
|
+
|
|
986
|
+
**缓解措施**:
|
|
987
|
+
- 每60秒清理超过5分钟不活跃的IP
|
|
988
|
+
- 可通过 `rate_limit.requests_per_minute: 0` 禁用速率限制
|
|
989
|
+
|
|
990
|
+
### 5. 中间件执行顺序
|
|
991
|
+
|
|
992
|
+
**行为**:中间件顺序为 `rate_limit → auth → i18n`,未认证请求会消耗速率限制配额。
|
|
993
|
+
|
|
994
|
+
**原因**:这是内部工具,攻击者场景不现实。顺序调整对正常使用几乎无影响。
|
|
995
|
+
|
|
996
|
+
**说明**:即使是未认证请求,也应该被速率限制,防止意外的大量请求(如前端bug导致的循环请求)。
|
|
997
|
+
|
|
998
|
+
### 6. 文件不存在时返回空数据
|
|
999
|
+
|
|
1000
|
+
**行为**:当 `keys.json` 文件不存在时,`_load_keys_data()` 返回 `{"keys": {}}` 而不是抛出异常。
|
|
1001
|
+
|
|
1002
|
+
**原因**:首次运行时,密钥文件尚未创建,这是有效状态。系统应优雅处理这种情况,而不是崩溃。
|
|
1003
|
+
|
|
1004
|
+
**实现**:在调用 `KeyStore.load()` 前检查文件是否存在:
|
|
1005
|
+
```python
|
|
1006
|
+
def _load_keys_data(config_override: dict | None = None) -> dict:
|
|
1007
|
+
cfg = config_override or config
|
|
1008
|
+
keys_path = Path(cfg["storage"]["keys_file"])
|
|
1009
|
+
if not keys_path.exists():
|
|
1010
|
+
return {"keys": {}}
|
|
1011
|
+
# ... 继原有逻辑
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
---
|
|
914
1015
|
|
|
915
1016
|
## 更新日志
|
|
916
1017
|
|
|
917
1018
|
|
|
1019
|
+
### v4.3.0 (2026-06-24)
|
|
1020
|
+
|
|
1021
|
+
- **测试覆盖率大幅提升**: 覆盖率从 79.15% 提升至 92.09%,新增 167 个测试
|
|
1022
|
+
- `detector.py`: 60% → 96% — 检测逻辑全覆盖,防止 v4.2.1 类 bug 回归
|
|
1023
|
+
- `web/routes/test.py`: 41% → 99% — 测试路由全覆盖
|
|
1024
|
+
- `web/routes/providers.py`: 37% → 100% — 提供商 CRUD 全覆盖
|
|
1025
|
+
- `web/routes/misc.py`: 53% → 100% — Webhook/日志/签名报告全覆盖
|
|
1026
|
+
- `web/routes/models.py`: 72% → 97% — 模型端点全覆盖
|
|
1027
|
+
- `web/routes/check.py`: 74% → 97% — 验证端点全覆盖
|
|
1028
|
+
|
|
1029
|
+
- **新增测试文件**:
|
|
1030
|
+
- `test_detector_unit.py` (31 tests) — 检测逻辑单元测试,覆盖 7 条核心路径
|
|
1031
|
+
- `test_routes_test.py` (48 tests) — 测试路由端点测试
|
|
1032
|
+
- `test_routes_providers.py` (32 tests) — 提供商 CRUD 路由测试
|
|
1033
|
+
- `test_routes_misc.py` (26 tests) — Webhook/日志/签名报告路由测试
|
|
1034
|
+
- `test_routes_models.py` (34 tests) — 模型列表/能力/SSE 路由测试
|
|
1035
|
+
- `test_routes_check.py` (23 tests) — 验证/SSRF/自动保存路由测试
|
|
1036
|
+
|
|
1037
|
+
- **检测逻辑回归测试**:
|
|
1038
|
+
- 锁定 `detect_provider()` 全部 7 条分支路径
|
|
1039
|
+
- 覆盖 v4.2.1 修复的两个 bug(格式匹配跳过验证、多候选直接返回)
|
|
1040
|
+
- 覆盖 402 余额不足识别、签名匹配阈值、超时处理等边界情况
|
|
1041
|
+
|
|
1042
|
+
- **Bug 修复**:
|
|
1043
|
+
- 修复 `test_tester.py` 中 `test_run_test_progress_callback` 缺失 `test_config` fixture 参数
|
|
1044
|
+
|
|
1045
|
+
|
|
1046
|
+
### v4.2.1 (2026-06-23)
|
|
1047
|
+
|
|
1048
|
+
- **检测逻辑修复**:
|
|
1049
|
+
- 修复格式匹配跳过验证的BUG:当有多个候选者时,不再直接返回,而是继续进行并发探测验证
|
|
1050
|
+
- 修复变量命名误导:`is_valid` 重命名为 `got_models`
|
|
1051
|
+
- 清理死代码:删除不可达的500分加分逻辑
|
|
1052
|
+
|
|
1053
|
+
- **删除/复制API认证修复**:
|
|
1054
|
+
- 修复 `deleteKey()` 和 `copyKey()` 使用原生 `fetch()` 导致认证失败的问题
|
|
1055
|
+
- 改为使用 `safeFetch()` 自动携带 Authorization header
|
|
1056
|
+
|
|
1057
|
+
### v4.2.0 (2026-06-23)
|
|
1058
|
+
|
|
1059
|
+
- **统一认证系统**: 复用加密密钥作为API认证token
|
|
1060
|
+
- 新增 `derive_api_token()` 函数,从加密密钥派生API token
|
|
1061
|
+
- 使用独立的 salt(`key-manager-api-auth-token-v1`)避免与存储加密交叉
|
|
1062
|
+
- PBKDF2HMAC 600K迭代,32字节token,缓存机制
|
|
1063
|
+
- 修改 `auth_middleware()` 自动fallback到派生token
|
|
1064
|
+
- 扩展认证白名单,静态文件不需要认证
|
|
1065
|
+
|
|
1066
|
+
- **前端自动携带token**: 无需手动配置
|
|
1067
|
+
- 在 `templates/index.html` 的 `<head>` 中注入 `window.__API_TOKEN__`
|
|
1068
|
+
- 修改 `safeFetch()` 自动添加 `Authorization: Bearer <token>` 头
|
|
1069
|
+
- 修改 `models.js` 的SSE请求也携带token
|
|
1070
|
+
- 在 `state.js` 中添加 `apiToken` 字段
|
|
1071
|
+
|
|
1072
|
+
- **单个密钥删除功能**:
|
|
1073
|
+
- 新增 `POST /api/keys/delete` API端点
|
|
1074
|
+
- 前端添加删除按钮(垃圾桶图标)
|
|
1075
|
+
- 使用项目标准的 `showConfirm` 确认框
|
|
1076
|
+
|
|
1077
|
+
- **自动保存检测密钥**:
|
|
1078
|
+
- 检测密钥后自动保存到注册表
|
|
1079
|
+
- 更新密钥状态和检测记录
|
|
1080
|
+
|
|
1081
|
+
- **复制完整密钥功能**:
|
|
1082
|
+
- 新增 `POST /api/keys/get-full-key` API端点
|
|
1083
|
+
- 前端点击密钥可复制完整密钥
|
|
1084
|
+
|
|
1085
|
+
- **测试验证**:
|
|
1086
|
+
- 全量测试通过:687 passed, 1 skipped
|
|
1087
|
+
- 覆盖率:75.72%(超过 60% 要求)
|
|
1088
|
+
- 新增 5 个 `derive_api_token()` 单元测试
|
|
1089
|
+
|
|
1090
|
+
- **可选加密开关**: 支持明文存储,便于本地开发
|
|
1091
|
+
- 新增 `storage.encrypted` 配置项(默认 `true`)
|
|
1092
|
+
- 设置 `encrypted: false` 可关闭加密,以明文 JSON 存储密钥
|
|
1093
|
+
- `load()` 自动检测文件格式,兼容已有加密/明文文件
|
|
1094
|
+
- 修复 `_load_keys_data()` 解密失败时抛出异常而非返回空字典(H1 修复)
|
|
1095
|
+
|
|
918
1096
|
### v4.1.0 (2026-06-22)
|
|
919
1097
|
|
|
920
1098
|
- **Web 模块技术债务优化**:
|