api-key-manager 4.0.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.0.0 → api_key_manager-4.3.0}/PKG-INFO +221 -10
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/README.md +217 -9
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/PKG-INFO +221 -10
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/SOURCES.txt +8 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/requires.txt +1 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/__init__.py +1 -1
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/api_models.py +1 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/config.py +1 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/detector.py +9 -13
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/errors.py +3 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/logger.py +26 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/storage.py +57 -10
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/__init__.py +12 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/_app.py +23 -11
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/middleware.py +21 -4
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/check.py +35 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/keys.py +77 -2
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/misc.py +14 -0
- api_key_manager-4.3.0/key_manager/web/utils.py +94 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/pyproject.toml +2 -1
- {api_key_manager-4.0.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.0.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.0.0 → api_key_manager-4.3.0}/tests/test_storage.py +217 -1
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_tester.py +1 -1
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/dependency_links.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/entry_points.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/api_key_manager.egg-info/top_level.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/__main__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/checker.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/cli.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/core.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/i18n.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/model_capabilities.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/parser.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/ai302.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/anthropic.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/baichuan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/base.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/cerebras.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/cohere.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/config_providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/cstcloud.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/dashscope_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/deepseek.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/dmxapi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/doubao.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/fireworks.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/google.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/grok.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/groq.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/huggingface.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/hyperbolic.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/infini.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/infini_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/kimi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/kimi_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/longcat.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/mimo.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/mimo_plan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/minimax.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/minimax_plan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/mistral.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/models_registry.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/modelscope.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/nvidia.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/ocoolai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/openai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/opencode.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/opencode_zen.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/openrouter.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/perplexity.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/poe.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/ppio.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/replicate.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/siliconflow.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/stepfun.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/tencent_hunyuan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/together.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/yi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/zai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/providers/zhipu_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/proxy.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/py.typed +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/ssrf.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/tester.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/url_override.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/validator.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/progress.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/balance.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/models.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/stats.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/web/routes/test.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/key_manager/webhook.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/setup.cfg +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_base_check.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_bug_fixes.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_checker.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_core.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_deepseek_balance.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_e2e.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_i18n.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_logger.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_openapi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_parser.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_provider_detection.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_provider_refactoring.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_proxy.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_security.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_token_limit_fast.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_v320_changes.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.3.0}/tests/test_validator.py +0 -0
- {api_key_manager-4.0.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
|
|
@@ -26,11 +27,13 @@ Requires-Dist: uvicorn>=0.32.0
|
|
|
26
27
|
Requires-Dist: python-multipart>=0.0.9
|
|
27
28
|
Requires-Dist: cryptography>=42.0.0
|
|
28
29
|
Requires-Dist: jinja2>=3.0.0
|
|
30
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
31
|
Provides-Extra: dev
|
|
30
32
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
31
33
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
32
34
|
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
|
|
33
35
|
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
34
37
|
|
|
35
38
|
# API Key Manager
|
|
36
39
|
|
|
@@ -236,7 +239,7 @@ python web.py
|
|
|
236
239
|
|
|
237
240
|
### 加密存储
|
|
238
241
|
|
|
239
|
-
API
|
|
242
|
+
API 密钥支持 AES-256-GCM 加密存储(默认开启),每次加密使用随机盐值:
|
|
240
243
|
|
|
241
244
|
```bash
|
|
242
245
|
# 设置加密密钥(环境变量)
|
|
@@ -256,6 +259,23 @@ python web.py
|
|
|
256
259
|
}
|
|
257
260
|
```
|
|
258
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
|
+
|
|
259
279
|
### 安全防护
|
|
260
280
|
|
|
261
281
|
- **路径遍历防护** - 导入端点验证路径在允许目录内
|
|
@@ -746,6 +766,11 @@ test:
|
|
|
746
766
|
- 10
|
|
747
767
|
- 20
|
|
748
768
|
|
|
769
|
+
# 存储设置
|
|
770
|
+
storage:
|
|
771
|
+
keys_file: "./data/keys.json"
|
|
772
|
+
encrypted: true # 设为 false 可关闭加密,以明文存储(便于本地开发)
|
|
773
|
+
|
|
749
774
|
# 认证设置
|
|
750
775
|
auth:
|
|
751
776
|
api_key: "your-secret-api-key" # API 认证
|
|
@@ -839,26 +864,28 @@ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
|
|
|
839
864
|
|
|
840
865
|
| 模块 | 测试文件 | 测试数 |
|
|
841
866
|
|------|---------|--------|
|
|
842
|
-
|
|
|
843
|
-
|
|
|
844
|
-
|
|
|
845
|
-
|
|
|
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 |
|
|
846
874
|
| 提供商合约 | `test_providers.py` | 220 |
|
|
875
|
+
| 密钥解析 | `test_parser.py` | 24 |
|
|
847
876
|
| 安全回归 | `test_security.py` | 21 |
|
|
848
877
|
| 加密存储 | `test_storage.py` | 26 |
|
|
849
878
|
| 错误系统 | `test_errors.py` | 28 |
|
|
850
879
|
| 国际化 | `test_i18n.py` | 37 |
|
|
851
|
-
| 端到端 | `test_e2e.py` | 18 |
|
|
852
880
|
| Webhook | `test_webhook.py` | 35 |
|
|
853
|
-
|
|
|
881
|
+
| 端到端 | `test_e2e.py` | 18 |
|
|
854
882
|
| 代理检测 | `test_proxy.py` | 19 |
|
|
855
883
|
| 日志系统 | `test_logger.py` | 21 |
|
|
856
|
-
| 能力测试 | `test_tester.py` | 11 |
|
|
857
884
|
| 核心门面 | `test_core.py` | 22 |
|
|
858
885
|
| Bug 修复 | `test_bug_fixes.py` | 15 |
|
|
859
886
|
| 提供商重构 | `test_provider_refactoring.py` | 32 |
|
|
860
887
|
|
|
861
|
-
**总测试数**:
|
|
888
|
+
**总测试数**: 913 | **覆盖率**: 92.09%
|
|
862
889
|
## SDK 使用
|
|
863
890
|
|
|
864
891
|
### Python SDK
|
|
@@ -945,10 +972,194 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
945
972
|
|
|
946
973
|
模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
|
|
947
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
|
+
---
|
|
948
1052
|
|
|
949
1053
|
## 更新日志
|
|
950
1054
|
|
|
951
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
|
+
|
|
1133
|
+
### v4.1.0 (2026-06-22)
|
|
1134
|
+
|
|
1135
|
+
- **Web 模块技术债务优化**:
|
|
1136
|
+
- 修复 `middleware.py` 中的死代码(`now - 300.0` 表达式未使用)
|
|
1137
|
+
- 提取 `build_chat_url()` 工具函数,消除 3 处重复的 Chat URL 构造逻辑
|
|
1138
|
+
- 提取 `resolve_provider()` 工具函数,消除 5 处重复的提供商检测+验证模式
|
|
1139
|
+
- 修复 `keys.py` 中弃用的 `datetime.utcnow()`,改用 `datetime.now(timezone.utc)`
|
|
1140
|
+
- 为所有静默异常块添加 `logger.debug()` 日志记录
|
|
1141
|
+
- 将内联导入(`import re as _re`、`import time as _time`)移至模块顶部
|
|
1142
|
+
- 统一 `test.py` 模型端点的错误响应格式,使用 `ErrorResponse` 替代原始 dict
|
|
1143
|
+
- 删除 `_app.py` 中的重复注释
|
|
1144
|
+
- 代码行数减少 ~110 行,新增 `web/utils.py` 共享工具模块
|
|
1145
|
+
|
|
1146
|
+
- **操作日志清理功能**:
|
|
1147
|
+
- 新增 `DELETE /api/logs` API 端点,支持清理当天或指定日期的主日志
|
|
1148
|
+
- 新增 `ProjectLogger.clear_main_log()` 方法
|
|
1149
|
+
|
|
1150
|
+
- **测试验证**:
|
|
1151
|
+
- 全量测试通过:682 passed, 1 skipped
|
|
1152
|
+
- 覆盖率:78.40%(超过 60% 要求)
|
|
1153
|
+
|
|
1154
|
+
- **前端清理优化**:
|
|
1155
|
+
- CSS: 添加缺失的 `--neon-cyan-dim` 变量,新增 `.btn-sm` `.btn-danger` `.url-input` `.toolbar-divider` 工具类
|
|
1156
|
+
- CSS: Confirm 模态框和添加服务商表单的内联样式提取为 CSS 类
|
|
1157
|
+
- CSS: 签名报告样式拆分到 `components/signature-report.css`
|
|
1158
|
+
- CSS: 内联 `style=` 从 71 减少到 39,`onfocus`/`onblur` 从 10 减少到 0
|
|
1159
|
+
- JS: 提取 `selectCustomOption`、`toggleCustomSelect`、`toggleLogs` 到 `ui-helpers.js`
|
|
1160
|
+
- JS: 新增 `clearLogs()` 函数,带确认对话框
|
|
1161
|
+
- HTML: `index.html` 从 531 行精简到 508 行
|
|
1162
|
+
|
|
952
1163
|
### v4.0.0 (2026-06-21)
|
|
953
1164
|
|
|
954
1165
|
- **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
|
|
@@ -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,194 @@ 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
|
+
|
|
1096
|
+
### v4.1.0 (2026-06-22)
|
|
1097
|
+
|
|
1098
|
+
- **Web 模块技术债务优化**:
|
|
1099
|
+
- 修复 `middleware.py` 中的死代码(`now - 300.0` 表达式未使用)
|
|
1100
|
+
- 提取 `build_chat_url()` 工具函数,消除 3 处重复的 Chat URL 构造逻辑
|
|
1101
|
+
- 提取 `resolve_provider()` 工具函数,消除 5 处重复的提供商检测+验证模式
|
|
1102
|
+
- 修复 `keys.py` 中弃用的 `datetime.utcnow()`,改用 `datetime.now(timezone.utc)`
|
|
1103
|
+
- 为所有静默异常块添加 `logger.debug()` 日志记录
|
|
1104
|
+
- 将内联导入(`import re as _re`、`import time as _time`)移至模块顶部
|
|
1105
|
+
- 统一 `test.py` 模型端点的错误响应格式,使用 `ErrorResponse` 替代原始 dict
|
|
1106
|
+
- 删除 `_app.py` 中的重复注释
|
|
1107
|
+
- 代码行数减少 ~110 行,新增 `web/utils.py` 共享工具模块
|
|
1108
|
+
|
|
1109
|
+
- **操作日志清理功能**:
|
|
1110
|
+
- 新增 `DELETE /api/logs` API 端点,支持清理当天或指定日期的主日志
|
|
1111
|
+
- 新增 `ProjectLogger.clear_main_log()` 方法
|
|
1112
|
+
|
|
1113
|
+
- **测试验证**:
|
|
1114
|
+
- 全量测试通过:682 passed, 1 skipped
|
|
1115
|
+
- 覆盖率:78.40%(超过 60% 要求)
|
|
1116
|
+
|
|
1117
|
+
- **前端清理优化**:
|
|
1118
|
+
- CSS: 添加缺失的 `--neon-cyan-dim` 变量,新增 `.btn-sm` `.btn-danger` `.url-input` `.toolbar-divider` 工具类
|
|
1119
|
+
- CSS: Confirm 模态框和添加服务商表单的内联样式提取为 CSS 类
|
|
1120
|
+
- CSS: 签名报告样式拆分到 `components/signature-report.css`
|
|
1121
|
+
- CSS: 内联 `style=` 从 71 减少到 39,`onfocus`/`onblur` 从 10 减少到 0
|
|
1122
|
+
- JS: 提取 `selectCustomOption`、`toggleCustomSelect`、`toggleLogs` 到 `ui-helpers.js`
|
|
1123
|
+
- JS: 新增 `clearLogs()` 函数,带确认对话框
|
|
1124
|
+
- HTML: `index.html` 从 531 行精简到 508 行
|
|
1125
|
+
|
|
918
1126
|
### v4.0.0 (2026-06-21)
|
|
919
1127
|
|
|
920
1128
|
- **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
|