mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
feat(auth): normalize OpenAI compatibility entries and enhance proxy configuration
- Added automatic trimming of API keys and migration of legacy `api-keys` to `api-key-entries`. - Introduced per-key `proxy-url` handling across OpenAI, Codex, and Claude API configurations. - Updated documentation to clarify usage of `proxy-url` with examples, ensuring backward compatibility. - Added normalization logic to reduce duplication and improve configuration consistency.
This commit is contained in:
@@ -95,7 +95,7 @@ If a plaintext key is detected in the config at startup, it will be bcrypt‑has
|
|||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
```json
|
```json
|
||||||
{"debug":true,"proxy-url":"","api-keys":["1...5","JS...W"],"quota-exceeded":{"switch-project":true,"switch-preview-model":true},"generative-language-api-key":["AI...01", "AI...02", "AI...03"],"request-log":true,"request-retry":3,"claude-api-key":[{"api-key":"cr...56","base-url":"https://example.com/api"},{"api-key":"cr...e3","base-url":"http://example.com:3000/api"},{"api-key":"sk-...q2","base-url":"https://example.com"}],"codex-api-key":[{"api-key":"sk...01","base-url":"https://example/v1"}],"openai-compatibility":[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":["sk...01"],"models":[{"name":"moonshotai/kimi-k2:free","alias":"kimi-k2"}]},{"name":"iflow","base-url":"https://apis.iflow.cn/v1","api-keys":["sk...7e"],"models":[{"name":"deepseek-v3.1","alias":"deepseek-v3.1"},{"name":"glm-4.5","alias":"glm-4.5"},{"name":"kimi-k2","alias":"kimi-k2"}]}]}
|
{"debug":true,"proxy-url":"","api-keys":["1...5","JS...W"],"quota-exceeded":{"switch-project":true,"switch-preview-model":true},"generative-language-api-key":["AI...01","AI...02","AI...03"],"request-log":true,"request-retry":3,"claude-api-key":[{"api-key":"cr...56","base-url":"https://example.com/api","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"cr...e3","base-url":"http://example.com:3000/api","proxy-url":""},{"api-key":"sk-...q2","base-url":"https://example.com","proxy-url":""}],"codex-api-key":[{"api-key":"sk...01","base-url":"https://example/v1","proxy-url":""}],"openai-compatibility":[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk...01","proxy-url":""}],"models":[{"name":"moonshotai/kimi-k2:free","alias":"kimi-k2"}]},{"name":"iflow","base-url":"https://apis.iflow.cn/v1","api-key-entries":[{"api-key":"sk...7e","proxy-url":"socks5://proxy.example.com:1080"}],"models":[{"name":"deepseek-v3.1","alias":"deepseek-v3.1"},{"name":"glm-4.5","alias":"glm-4.5"},{"name":"kimi-k2","alias":"kimi-k2"}]}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
@@ -335,14 +335,14 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
```json
|
```json
|
||||||
{ "codex-api-key": [ { "api-key": "sk-a", "base-url": "" } ] }
|
{ "codex-api-key": [ { "api-key": "sk-a", "base-url": "", "proxy-url": "" } ] }
|
||||||
```
|
```
|
||||||
- PUT `/codex-api-key` — Replace the list
|
- PUT `/codex-api-key` — Replace the list
|
||||||
- Request:
|
- Request:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"api-key":"sk-a"},{"api-key":"sk-b","base-url":"https://c.example.com"}]' \
|
-d '[{"api-key":"sk-a","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"sk-b","base-url":"https://c.example.com","proxy-url":""}]' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
@@ -354,14 +354,14 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com"}}' \
|
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com","proxy-url":""}}' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- Request (by match):
|
- Request (by match):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":""}}' \
|
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":"","proxy-url":"socks5://proxy.example.com:1080"}}' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
@@ -436,14 +436,14 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
```json
|
```json
|
||||||
{ "claude-api-key": [ { "api-key": "sk-a", "base-url": "" } ] }
|
{ "claude-api-key": [ { "api-key": "sk-a", "base-url": "", "proxy-url": "" } ] }
|
||||||
```
|
```
|
||||||
- PUT `/claude-api-key` — Replace the list
|
- PUT `/claude-api-key` — Replace the list
|
||||||
- Request:
|
- Request:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"api-key":"sk-a"},{"api-key":"sk-b","base-url":"https://c.example.com"}]' \
|
-d '[{"api-key":"sk-a","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"sk-b","base-url":"https://c.example.com","proxy-url":""}]' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
@@ -455,14 +455,14 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com"}}' \
|
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com","proxy-url":""}}' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- Request (by match):
|
- Request (by match):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":""}}' \
|
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":"","proxy-url":"socks5://proxy.example.com:1080"}}' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
@@ -491,14 +491,14 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
```json
|
```json
|
||||||
{ "openai-compatibility": [ { "name": "openrouter", "base-url": "https://openrouter.ai/api/v1", "api-keys": [], "models": [] } ] }
|
{ "openai-compatibility": [ { "name": "openrouter", "base-url": "https://openrouter.ai/api/v1", "api-key-entries": [ { "api-key": "sk", "proxy-url": "" } ], "models": [] } ] }
|
||||||
```
|
```
|
||||||
- PUT `/openai-compatibility` — Replace the list
|
- PUT `/openai-compatibility` — Replace the list
|
||||||
- Request:
|
- Request:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":["sk"],"models":[{"name":"m","alias":"a"}]}]' \
|
-d '[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[{"name":"m","alias":"a"}]}]' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
@@ -510,20 +510,23 @@ These endpoints update the inline `config-api-key` provider inside the `auth.pro
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"name":"openrouter","value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":[],"models":[]}}' \
|
-d '{"name":"openrouter","value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[]}}' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- Request (by index):
|
- Request (by index):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":0,"value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":[],"models":[]}}' \
|
-d '{"index":0,"value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[]}}' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- Response:
|
- Response:
|
||||||
```json
|
```json
|
||||||
{ "status": "ok" }
|
{ "status": "ok" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Notes:
|
||||||
|
- Legacy `api-keys` input remains accepted; keys are migrated into `api-key-entries` automatically so the legacy field will eventually remain empty in responses.
|
||||||
- DELETE `/openai-compatibility` — Delete (`?name=` or `?index=`)
|
- DELETE `/openai-compatibility` — Delete (`?name=` or `?index=`)
|
||||||
- Request (by name):
|
- Request (by name):
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
```json
|
```json
|
||||||
{"debug":true,"proxy-url":"","api-keys":["1...5","JS...W"],"quota-exceeded":{"switch-project":true,"switch-preview-model":true},"generative-language-api-key":["AI...01", "AI...02", "AI...03"],"request-log":true,"request-retry":3,"claude-api-key":[{"api-key":"cr...56","base-url":"https://example.com/api"},{"api-key":"cr...e3","base-url":"http://example.com:3000/api"},{"api-key":"sk-...q2","base-url":"https://example.com"}],"codex-api-key":[{"api-key":"sk...01","base-url":"https://example/v1"}],"openai-compatibility":[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":["sk...01"],"models":[{"name":"moonshotai/kimi-k2:free","alias":"kimi-k2"}]},{"name":"iflow","base-url":"https://apis.iflow.cn/v1","api-keys":["sk...7e"],"models":[{"name":"deepseek-v3.1","alias":"deepseek-v3.1"},{"name":"glm-4.5","alias":"glm-4.5"},{"name":"kimi-k2","alias":"kimi-k2"}]}]}
|
{"debug":true,"proxy-url":"","api-keys":["1...5","JS...W"],"quota-exceeded":{"switch-project":true,"switch-preview-model":true},"generative-language-api-key":["AI...01","AI...02","AI...03"],"request-log":true,"request-retry":3,"claude-api-key":[{"api-key":"cr...56","base-url":"https://example.com/api","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"cr...e3","base-url":"http://example.com:3000/api","proxy-url":""},{"api-key":"sk-...q2","base-url":"https://example.com","proxy-url":""}],"codex-api-key":[{"api-key":"sk...01","base-url":"https://example/v1","proxy-url":""}],"openai-compatibility":[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk...01","proxy-url":""}],"models":[{"name":"moonshotai/kimi-k2:free","alias":"kimi-k2"}]},{"name":"iflow","base-url":"https://apis.iflow.cn/v1","api-key-entries":[{"api-key":"sk...7e","proxy-url":"socks5://proxy.example.com:1080"}],"models":[{"name":"deepseek-v3.1","alias":"deepseek-v3.1"},{"name":"glm-4.5","alias":"glm-4.5"},{"name":"kimi-k2","alias":"kimi-k2"}]}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
@@ -335,14 +335,14 @@
|
|||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
```json
|
```json
|
||||||
{ "codex-api-key": [ { "api-key": "sk-a", "base-url": "" } ] }
|
{ "codex-api-key": [ { "api-key": "sk-a", "base-url": "", "proxy-url": "" } ] }
|
||||||
```
|
```
|
||||||
- PUT `/codex-api-key` — 完整改写列表
|
- PUT `/codex-api-key` — 完整改写列表
|
||||||
- 请求:
|
- 请求:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"api-key":"sk-a"},{"api-key":"sk-b","base-url":"https://c.example.com"}]' \
|
-d '[{"api-key":"sk-a","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"sk-b","base-url":"https://c.example.com","proxy-url":""}]' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
@@ -354,14 +354,14 @@
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com"}}' \
|
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com","proxy-url":""}}' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- 请求(按匹配):
|
- 请求(按匹配):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":""}}' \
|
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":"","proxy-url":"socks5://proxy.example.com:1080"}}' \
|
||||||
http://localhost:8317/v0/management/codex-api-key
|
http://localhost:8317/v0/management/codex-api-key
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
@@ -436,14 +436,14 @@
|
|||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
```json
|
```json
|
||||||
{ "claude-api-key": [ { "api-key": "sk-a", "base-url": "" } ] }
|
{ "claude-api-key": [ { "api-key": "sk-a", "base-url": "", "proxy-url": "" } ] }
|
||||||
```
|
```
|
||||||
- PUT `/claude-api-key` — 完整改写列表
|
- PUT `/claude-api-key` — 完整改写列表
|
||||||
- 请求:
|
- 请求:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"api-key":"sk-a"},{"api-key":"sk-b","base-url":"https://c.example.com"}]' \
|
-d '[{"api-key":"sk-a","proxy-url":"socks5://proxy.example.com:1080"},{"api-key":"sk-b","base-url":"https://c.example.com","proxy-url":""}]' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
@@ -455,14 +455,14 @@
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com"}}' \
|
-d '{"index":1,"value":{"api-key":"sk-b2","base-url":"https://c.example.com","proxy-url":""}}' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- 请求(按匹配):
|
- 请求(按匹配):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":""}}' \
|
-d '{"match":"sk-a","value":{"api-key":"sk-a","base-url":"","proxy-url":"socks5://proxy.example.com:1080"}}' \
|
||||||
http://localhost:8317/v0/management/claude-api-key
|
http://localhost:8317/v0/management/claude-api-key
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
@@ -491,14 +491,14 @@
|
|||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
```json
|
```json
|
||||||
{ "openai-compatibility": [ { "name": "openrouter", "base-url": "https://openrouter.ai/api/v1", "api-keys": [], "models": [] } ] }
|
{ "openai-compatibility": [ { "name": "openrouter", "base-url": "https://openrouter.ai/api/v1", "api-key-entries": [ { "api-key": "sk", "proxy-url": "" } ], "models": [] } ] }
|
||||||
```
|
```
|
||||||
- PUT `/openai-compatibility` — 完整改写列表
|
- PUT `/openai-compatibility` — 完整改写列表
|
||||||
- 请求:
|
- 请求:
|
||||||
```bash
|
```bash
|
||||||
curl -X PUT -H 'Content-Type: application/json' \
|
curl -X PUT -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":["sk"],"models":[{"name":"m","alias":"a"}]}]' \
|
-d '[{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[{"name":"m","alias":"a"}]}]' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
@@ -510,20 +510,23 @@
|
|||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"name":"openrouter","value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":[],"models":[]}}' \
|
-d '{"name":"openrouter","value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[]}}' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- 请求(按索引):
|
- 请求(按索引):
|
||||||
```bash
|
```bash
|
||||||
curl -X PATCH -H 'Content-Type: application/json' \
|
curl -X PATCH -H 'Content-Type: application/json' \
|
||||||
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
-H 'Authorization: Bearer <MANAGEMENT_KEY>' \
|
||||||
-d '{"index":0,"value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-keys":[],"models":[]}}' \
|
-d '{"index":0,"value":{"name":"openrouter","base-url":"https://openrouter.ai/api/v1","api-key-entries":[{"api-key":"sk","proxy-url":""}],"models":[]}}' \
|
||||||
http://localhost:8317/v0/management/openai-compatibility
|
http://localhost:8317/v0/management/openai-compatibility
|
||||||
```
|
```
|
||||||
- 响应:
|
- 响应:
|
||||||
```json
|
```json
|
||||||
{ "status": "ok" }
|
{ "status": "ok" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- 说明:
|
||||||
|
- 仍可提交遗留的 `api-keys` 字段,但所有密钥会自动迁移到 `api-key-entries` 中,返回结果中的 `api-keys` 会逐步留空。
|
||||||
- DELETE `/openai-compatibility` — 删除(`?name=` 或 `?index=`)
|
- DELETE `/openai-compatibility` — 删除(`?name=` 或 `?index=`)
|
||||||
- 请求(按名称):
|
- 请求(按名称):
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package management
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -221,6 +222,9 @@ func (h *Handler) PutOpenAICompat(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
arr = obj.Items
|
arr = obj.Items
|
||||||
}
|
}
|
||||||
|
for i := range arr {
|
||||||
|
normalizeOpenAICompatibilityEntry(&arr[i])
|
||||||
|
}
|
||||||
h.cfg.OpenAICompatibility = arr
|
h.cfg.OpenAICompatibility = arr
|
||||||
h.persist(c)
|
h.persist(c)
|
||||||
}
|
}
|
||||||
@@ -234,6 +238,7 @@ func (h *Handler) PatchOpenAICompat(c *gin.Context) {
|
|||||||
c.JSON(400, gin.H{"error": "invalid body"})
|
c.JSON(400, gin.H{"error": "invalid body"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
normalizeOpenAICompatibilityEntry(body.Value)
|
||||||
if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.OpenAICompatibility) {
|
if body.Index != nil && *body.Index >= 0 && *body.Index < len(h.cfg.OpenAICompatibility) {
|
||||||
h.cfg.OpenAICompatibility[*body.Index] = *body.Value
|
h.cfg.OpenAICompatibility[*body.Index] = *body.Value
|
||||||
h.persist(c)
|
h.persist(c)
|
||||||
@@ -347,3 +352,32 @@ func (h *Handler) DeleteCodexKey(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(400, gin.H{"error": "missing api-key or index"})
|
c.JSON(400, gin.H{"error": "missing api-key or index"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeOpenAICompatibilityEntry(entry *config.OpenAICompatibility) {
|
||||||
|
if entry == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
existing := make(map[string]struct{}, len(entry.APIKeyEntries))
|
||||||
|
for i := range entry.APIKeyEntries {
|
||||||
|
trimmed := strings.TrimSpace(entry.APIKeyEntries[i].APIKey)
|
||||||
|
entry.APIKeyEntries[i].APIKey = trimmed
|
||||||
|
if trimmed != "" {
|
||||||
|
existing[trimmed] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(entry.APIKeys) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, legacyKey := range entry.APIKeys {
|
||||||
|
trimmed := strings.TrimSpace(legacyKey)
|
||||||
|
if trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := existing[trimmed]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry.APIKeyEntries = append(entry.APIKeyEntries, config.OpenAICompatibilityAPIKey{APIKey: trimmed})
|
||||||
|
existing[trimmed] = struct{}{}
|
||||||
|
}
|
||||||
|
entry.APIKeys = nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -626,7 +626,12 @@ func (s *Server) UpdateClients(cfg *config.Config) {
|
|||||||
codexAPIKeyCount := len(cfg.CodexKey)
|
codexAPIKeyCount := len(cfg.CodexKey)
|
||||||
openAICompatCount := 0
|
openAICompatCount := 0
|
||||||
for i := range cfg.OpenAICompatibility {
|
for i := range cfg.OpenAICompatibility {
|
||||||
openAICompatCount += len(cfg.OpenAICompatibility[i].APIKeys)
|
entry := cfg.OpenAICompatibility[i]
|
||||||
|
if len(entry.APIKeyEntries) > 0 {
|
||||||
|
openAICompatCount += len(entry.APIKeyEntries)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
openAICompatCount += len(entry.APIKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
total := authFiles + glAPIKeyCount + claudeAPIKeyCount + codexAPIKeyCount + openAICompatCount
|
total := authFiles + glAPIKeyCount + claudeAPIKeyCount + codexAPIKeyCount + openAICompatCount
|
||||||
|
|||||||
Reference in New Issue
Block a user