api-key-manager 4.0.0__tar.gz → 4.1.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.0.0 → api_key_manager-4.1.0}/PKG-INFO +32 -1
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/README.md +30 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/PKG-INFO +32 -1
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/SOURCES.txt +1 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/requires.txt +1 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/logger.py +26 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/middleware.py +1 -1
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/keys.py +2 -2
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/misc.py +14 -0
- api_key_manager-4.1.0/key_manager/web/utils.py +94 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/pyproject.toml +2 -1
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/dependency_links.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/entry_points.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/top_level.txt +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/__main__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/api_models.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/checker.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/cli.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/config.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/core.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/detector.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/errors.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/i18n.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/model_capabilities.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/parser.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/ai302.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/anthropic.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/baichuan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/base.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/cerebras.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/cohere.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/config_providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/cstcloud.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/dashscope.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/dashscope_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/deepseek.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/dmxapi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/doubao.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/fireworks.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/google.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/grok.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/groq.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/huggingface.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/hyperbolic.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/infini.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/infini_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/kimi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/kimi_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/longcat.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/mimo.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/mimo_plan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/minimax.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/minimax_plan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/mistral.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/models_registry.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/modelscope.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/nvidia.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/ocoolai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/openai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/opencode.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/opencode_zen.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/openrouter.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/perplexity.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/poe.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/ppio.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/replicate.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/siliconflow.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/stepfun.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/tencent_hunyuan.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/together.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/yi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/zai.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/zhipu.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/providers/zhipu_coding.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/proxy.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/py.typed +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/ssrf.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/storage.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/tester.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/url_override.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/validator.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/_app.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/progress.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/__init__.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/balance.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/check.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/models.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/stats.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/web/routes/test.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/key_manager/webhook.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/setup.cfg +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_api_endpoints.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_base_check.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_bug_fixes.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_checker.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_core.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_deepseek_balance.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_e2e.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_errors.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_i18n.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_logger.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_openapi.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_parser.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_provider_detection.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_provider_refactoring.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_providers.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_proxy.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_security.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_storage.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_tester.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_token_limit_fast.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_v320_changes.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_validator.py +0 -0
- {api_key_manager-4.0.0 → api_key_manager-4.1.0}/tests/test_webhook.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: api-key-manager
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.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
|
|
@@ -26,6 +26,7 @@ Requires-Dist: uvicorn>=0.32.0
|
|
|
26
26
|
Requires-Dist: python-multipart>=0.0.9
|
|
27
27
|
Requires-Dist: cryptography>=42.0.0
|
|
28
28
|
Requires-Dist: jinja2>=3.0.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
31
32
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
@@ -949,6 +950,36 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
949
950
|
## 更新日志
|
|
950
951
|
|
|
951
952
|
|
|
953
|
+
### v4.1.0 (2026-06-22)
|
|
954
|
+
|
|
955
|
+
- **Web 模块技术债务优化**:
|
|
956
|
+
- 修复 `middleware.py` 中的死代码(`now - 300.0` 表达式未使用)
|
|
957
|
+
- 提取 `build_chat_url()` 工具函数,消除 3 处重复的 Chat URL 构造逻辑
|
|
958
|
+
- 提取 `resolve_provider()` 工具函数,消除 5 处重复的提供商检测+验证模式
|
|
959
|
+
- 修复 `keys.py` 中弃用的 `datetime.utcnow()`,改用 `datetime.now(timezone.utc)`
|
|
960
|
+
- 为所有静默异常块添加 `logger.debug()` 日志记录
|
|
961
|
+
- 将内联导入(`import re as _re`、`import time as _time`)移至模块顶部
|
|
962
|
+
- 统一 `test.py` 模型端点的错误响应格式,使用 `ErrorResponse` 替代原始 dict
|
|
963
|
+
- 删除 `_app.py` 中的重复注释
|
|
964
|
+
- 代码行数减少 ~110 行,新增 `web/utils.py` 共享工具模块
|
|
965
|
+
|
|
966
|
+
- **操作日志清理功能**:
|
|
967
|
+
- 新增 `DELETE /api/logs` API 端点,支持清理当天或指定日期的主日志
|
|
968
|
+
- 新增 `ProjectLogger.clear_main_log()` 方法
|
|
969
|
+
|
|
970
|
+
- **测试验证**:
|
|
971
|
+
- 全量测试通过:682 passed, 1 skipped
|
|
972
|
+
- 覆盖率:78.40%(超过 60% 要求)
|
|
973
|
+
|
|
974
|
+
- **前端清理优化**:
|
|
975
|
+
- CSS: 添加缺失的 `--neon-cyan-dim` 变量,新增 `.btn-sm` `.btn-danger` `.url-input` `.toolbar-divider` 工具类
|
|
976
|
+
- CSS: Confirm 模态框和添加服务商表单的内联样式提取为 CSS 类
|
|
977
|
+
- CSS: 签名报告样式拆分到 `components/signature-report.css`
|
|
978
|
+
- CSS: 内联 `style=` 从 71 减少到 39,`onfocus`/`onblur` 从 10 减少到 0
|
|
979
|
+
- JS: 提取 `selectCustomOption`、`toggleCustomSelect`、`toggleLogs` 到 `ui-helpers.js`
|
|
980
|
+
- JS: 新增 `clearLogs()` 函数,带确认对话框
|
|
981
|
+
- HTML: `index.html` 从 531 行精简到 508 行
|
|
982
|
+
|
|
952
983
|
### v4.0.0 (2026-06-21)
|
|
953
984
|
|
|
954
985
|
- **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
|
|
@@ -915,6 +915,36 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
915
915
|
## 更新日志
|
|
916
916
|
|
|
917
917
|
|
|
918
|
+
### v4.1.0 (2026-06-22)
|
|
919
|
+
|
|
920
|
+
- **Web 模块技术债务优化**:
|
|
921
|
+
- 修复 `middleware.py` 中的死代码(`now - 300.0` 表达式未使用)
|
|
922
|
+
- 提取 `build_chat_url()` 工具函数,消除 3 处重复的 Chat URL 构造逻辑
|
|
923
|
+
- 提取 `resolve_provider()` 工具函数,消除 5 处重复的提供商检测+验证模式
|
|
924
|
+
- 修复 `keys.py` 中弃用的 `datetime.utcnow()`,改用 `datetime.now(timezone.utc)`
|
|
925
|
+
- 为所有静默异常块添加 `logger.debug()` 日志记录
|
|
926
|
+
- 将内联导入(`import re as _re`、`import time as _time`)移至模块顶部
|
|
927
|
+
- 统一 `test.py` 模型端点的错误响应格式,使用 `ErrorResponse` 替代原始 dict
|
|
928
|
+
- 删除 `_app.py` 中的重复注释
|
|
929
|
+
- 代码行数减少 ~110 行,新增 `web/utils.py` 共享工具模块
|
|
930
|
+
|
|
931
|
+
- **操作日志清理功能**:
|
|
932
|
+
- 新增 `DELETE /api/logs` API 端点,支持清理当天或指定日期的主日志
|
|
933
|
+
- 新增 `ProjectLogger.clear_main_log()` 方法
|
|
934
|
+
|
|
935
|
+
- **测试验证**:
|
|
936
|
+
- 全量测试通过:682 passed, 1 skipped
|
|
937
|
+
- 覆盖率:78.40%(超过 60% 要求)
|
|
938
|
+
|
|
939
|
+
- **前端清理优化**:
|
|
940
|
+
- CSS: 添加缺失的 `--neon-cyan-dim` 变量,新增 `.btn-sm` `.btn-danger` `.url-input` `.toolbar-divider` 工具类
|
|
941
|
+
- CSS: Confirm 模态框和添加服务商表单的内联样式提取为 CSS 类
|
|
942
|
+
- CSS: 签名报告样式拆分到 `components/signature-report.css`
|
|
943
|
+
- CSS: 内联 `style=` 从 71 减少到 39,`onfocus`/`onblur` 从 10 减少到 0
|
|
944
|
+
- JS: 提取 `selectCustomOption`、`toggleCustomSelect`、`toggleLogs` 到 `ui-helpers.js`
|
|
945
|
+
- JS: 新增 `clearLogs()` 函数,带确认对话框
|
|
946
|
+
- HTML: `index.html` 从 531 行精简到 508 行
|
|
947
|
+
|
|
918
948
|
### v4.0.0 (2026-06-21)
|
|
919
949
|
|
|
920
950
|
- **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: api-key-manager
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.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
|
|
@@ -26,6 +26,7 @@ Requires-Dist: uvicorn>=0.32.0
|
|
|
26
26
|
Requires-Dist: python-multipart>=0.0.9
|
|
27
27
|
Requires-Dist: cryptography>=42.0.0
|
|
28
28
|
Requires-Dist: jinja2>=3.0.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
31
32
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
|
|
@@ -949,6 +950,36 @@ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
|
|
|
949
950
|
## 更新日志
|
|
950
951
|
|
|
951
952
|
|
|
953
|
+
### v4.1.0 (2026-06-22)
|
|
954
|
+
|
|
955
|
+
- **Web 模块技术债务优化**:
|
|
956
|
+
- 修复 `middleware.py` 中的死代码(`now - 300.0` 表达式未使用)
|
|
957
|
+
- 提取 `build_chat_url()` 工具函数,消除 3 处重复的 Chat URL 构造逻辑
|
|
958
|
+
- 提取 `resolve_provider()` 工具函数,消除 5 处重复的提供商检测+验证模式
|
|
959
|
+
- 修复 `keys.py` 中弃用的 `datetime.utcnow()`,改用 `datetime.now(timezone.utc)`
|
|
960
|
+
- 为所有静默异常块添加 `logger.debug()` 日志记录
|
|
961
|
+
- 将内联导入(`import re as _re`、`import time as _time`)移至模块顶部
|
|
962
|
+
- 统一 `test.py` 模型端点的错误响应格式,使用 `ErrorResponse` 替代原始 dict
|
|
963
|
+
- 删除 `_app.py` 中的重复注释
|
|
964
|
+
- 代码行数减少 ~110 行,新增 `web/utils.py` 共享工具模块
|
|
965
|
+
|
|
966
|
+
- **操作日志清理功能**:
|
|
967
|
+
- 新增 `DELETE /api/logs` API 端点,支持清理当天或指定日期的主日志
|
|
968
|
+
- 新增 `ProjectLogger.clear_main_log()` 方法
|
|
969
|
+
|
|
970
|
+
- **测试验证**:
|
|
971
|
+
- 全量测试通过:682 passed, 1 skipped
|
|
972
|
+
- 覆盖率:78.40%(超过 60% 要求)
|
|
973
|
+
|
|
974
|
+
- **前端清理优化**:
|
|
975
|
+
- CSS: 添加缺失的 `--neon-cyan-dim` 变量,新增 `.btn-sm` `.btn-danger` `.url-input` `.toolbar-divider` 工具类
|
|
976
|
+
- CSS: Confirm 模态框和添加服务商表单的内联样式提取为 CSS 类
|
|
977
|
+
- CSS: 签名报告样式拆分到 `components/signature-report.css`
|
|
978
|
+
- CSS: 内联 `style=` 从 71 减少到 39,`onfocus`/`onblur` 从 10 减少到 0
|
|
979
|
+
- JS: 提取 `selectCustomOption`、`toggleCustomSelect`、`toggleLogs` 到 `ui-helpers.js`
|
|
980
|
+
- JS: 新增 `clearLogs()` 函数,带确认对话框
|
|
981
|
+
- HTML: `index.html` 从 531 行精简到 508 行
|
|
982
|
+
|
|
952
983
|
### v4.0.0 (2026-06-21)
|
|
953
984
|
|
|
954
985
|
- **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
|
|
@@ -202,5 +202,31 @@ class ProjectLogger:
|
|
|
202
202
|
return files
|
|
203
203
|
|
|
204
204
|
|
|
205
|
+
def clear_main_log(self, date: str = None) -> dict:
|
|
206
|
+
"""Clear main log file for specified date (default: today).
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
date: Date string in YYYY-MM-DD format (default: today)
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
dict with 'success', 'date', 'deleted_lines' keys
|
|
213
|
+
"""
|
|
214
|
+
if date is None:
|
|
215
|
+
date = datetime.now().strftime("%Y-%m-%d")
|
|
216
|
+
|
|
217
|
+
log_file = self.logs_dir / f"main_{date}.log"
|
|
218
|
+
if not log_file.exists():
|
|
219
|
+
return {"success": False, "date": date, "deleted_lines": 0, "error": "Log file not found"}
|
|
220
|
+
|
|
221
|
+
# Count lines before clearing
|
|
222
|
+
with open(log_file, encoding="utf-8") as f:
|
|
223
|
+
lines_count = sum(1 for _ in f)
|
|
224
|
+
|
|
225
|
+
# Clear the file
|
|
226
|
+
log_file.write_text("", encoding="utf-8")
|
|
227
|
+
|
|
228
|
+
return {"success": True, "date": date, "deleted_lines": lines_count}
|
|
229
|
+
|
|
230
|
+
|
|
205
231
|
# Global logger instance
|
|
206
232
|
project_logger = ProjectLogger()
|
|
@@ -48,7 +48,7 @@ def _cleanup_rate_limit_store():
|
|
|
48
48
|
ips_to_remove = []
|
|
49
49
|
for ip, timestamps in _RATE_LIMIT_STORE.items():
|
|
50
50
|
# Remove old timestamps
|
|
51
|
-
_RATE_LIMIT_STORE[ip] = [t for t in timestamps if now - t <
|
|
51
|
+
_RATE_LIMIT_STORE[ip] = [t for t in timestamps if now - t < 300.0]
|
|
52
52
|
# If no recent timestamps, mark for removal
|
|
53
53
|
if not _RATE_LIMIT_STORE[ip]:
|
|
54
54
|
ips_to_remove.append(ip)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Key management routes: import, list, export, clear."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from fastapi import APIRouter, Query, Request
|
|
@@ -32,7 +32,7 @@ async def api_import(body: ImportRequest):
|
|
|
32
32
|
|
|
33
33
|
if body.batch:
|
|
34
34
|
# Inline batch import
|
|
35
|
-
timestamp = datetime.
|
|
35
|
+
timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
36
36
|
new = 0
|
|
37
37
|
dupes = 0
|
|
38
38
|
errors: list[str] = []
|
|
@@ -64,6 +64,20 @@ async def api_logs_operations():
|
|
|
64
64
|
return OperationsResponse(operations=[OperationEntry(**entry) for entry in operations])
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
@router.delete("/api/logs", tags=["Logs"])
|
|
68
|
+
async def api_logs_clear(date: str = None):
|
|
69
|
+
"""Clear main log file for specified date (default: today).
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
date: Date string in YYYY-MM-DD format (optional, default: today)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
dict with success status and details
|
|
76
|
+
"""
|
|
77
|
+
result = project_logger.clear_main_log(date)
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
|
|
67
81
|
# ---
|
|
68
82
|
# PROGRESS
|
|
69
83
|
# ---
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Shared utility functions for the web module.
|
|
2
|
+
|
|
3
|
+
This module contains small, reusable helpers used across multiple
|
|
4
|
+
web routes and modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from key_manager.providers.base import ProviderBase
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_chat_url(provider: "ProviderBase") -> str:
|
|
15
|
+
"""Build chat completions URL from provider's check_endpoint.
|
|
16
|
+
|
|
17
|
+
Extracts version prefix (e.g., ``/v1``) from ``check_endpoint`` and
|
|
18
|
+
constructs the full chat completions URL by appending ``/chat/completions``.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
provider: Provider object with ``check_endpoint`` and
|
|
22
|
+
``get_base_url()`` methods.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Full chat completions URL string.
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
>>> # Provider with versioned endpoint
|
|
29
|
+
>>> provider.check_endpoint = "/v1/models"
|
|
30
|
+
>>> build_chat_url(provider)
|
|
31
|
+
'https://api.example.com/v1/chat/completions'
|
|
32
|
+
|
|
33
|
+
>>> # Provider without version prefix
|
|
34
|
+
>>> provider.check_endpoint = "/models"
|
|
35
|
+
>>> build_chat_url(provider)
|
|
36
|
+
'https://api.example.com/chat/completions'
|
|
37
|
+
"""
|
|
38
|
+
version_match = re.match(r"(/v\d+)", provider.check_endpoint or "")
|
|
39
|
+
version_prefix = version_match.group(1) if version_match else ""
|
|
40
|
+
return f"{provider.get_base_url()}{version_prefix}/chat/completions"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def resolve_provider(
|
|
44
|
+
key: str,
|
|
45
|
+
provider_name: str | None,
|
|
46
|
+
timeout: int,
|
|
47
|
+
proxy: str | None,
|
|
48
|
+
providers: dict,
|
|
49
|
+
detect_provider_fn,
|
|
50
|
+
) -> tuple[str, object]:
|
|
51
|
+
"""Resolve provider name and object from key.
|
|
52
|
+
|
|
53
|
+
Handles the common pattern of:
|
|
54
|
+
1. If provider_name not given, detect it from the key
|
|
55
|
+
2. Validate the provider name is known
|
|
56
|
+
3. Get the provider object
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
key: The API key to check
|
|
60
|
+
provider_name: Explicit provider name, or None to auto-detect
|
|
61
|
+
timeout: HTTP timeout in seconds for detection requests
|
|
62
|
+
proxy: Optional proxy URL
|
|
63
|
+
providers: Dict of provider_name -> provider_object
|
|
64
|
+
detect_provider_fn: Async function to detect provider from key
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
tuple: (provider_name, provider_obj)
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
ValidationError: If provider cannot be determined or is unknown
|
|
71
|
+
"""
|
|
72
|
+
import httpx
|
|
73
|
+
|
|
74
|
+
from key_manager.errors import ErrorCode, ValidationError
|
|
75
|
+
from key_manager.i18n import t
|
|
76
|
+
|
|
77
|
+
if not provider_name:
|
|
78
|
+
async with httpx.AsyncClient(timeout=timeout, proxy=proxy or None) as client:
|
|
79
|
+
provider_name = await detect_provider_fn(client, key)
|
|
80
|
+
if not provider_name or provider_name == 'unknown':
|
|
81
|
+
raise ValidationError(
|
|
82
|
+
code=ErrorCode.VALIDATION_PROVIDER_UNKNOWN,
|
|
83
|
+
message=t("VALIDATION_PROVIDER_UNKNOWN"),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
provider_name_lower = provider_name.lower()
|
|
87
|
+
provider_obj = providers.get(provider_name_lower)
|
|
88
|
+
if not provider_obj:
|
|
89
|
+
raise ValidationError(
|
|
90
|
+
code=ErrorCode.VALIDATION_PROVIDER_UNKNOWN,
|
|
91
|
+
message=t("VALIDATION_PROVIDER_UNKNOWN"),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return provider_name, provider_obj
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "api-key-manager"
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.1.0"
|
|
8
8
|
description = "Batch manage API keys for 45+ AI providers with CLI and Web interfaces"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -32,6 +32,7 @@ dependencies = [
|
|
|
32
32
|
"python-multipart>=0.0.9",
|
|
33
33
|
"cryptography>=42.0.0",
|
|
34
34
|
"jinja2>=3.0.0",
|
|
35
|
+
"pydantic>=2.0.0",
|
|
35
36
|
]
|
|
36
37
|
|
|
37
38
|
[project.optional-dependencies]
|
{api_key_manager-4.0.0 → api_key_manager-4.1.0}/api_key_manager.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|