Skip to content
Open
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,11 @@ QWEN_CHAT_PROXY_URL=
QWEN_CLI_PROXY_URL=

# HTTP/HTTPS 代理配置
# 支持 HTTP, HTTPS, SOCKS5 代理
# 支持 HTTP, HTTPS, SOCKS5, SOCKS5H 代理(socks5h:// 由代理端解析 DNS)
# 全局回退代理:当账号未配置专属代理时使用(账号级代理可在 dashboard 或 ACCOUNTS 中设置)
# 优先级:account.proxy > PROXY_URL > 不使用代理
# HTTP/HTTPS proxy configuration
# Supports HTTP, HTTPS, SOCKS5 proxies
# Supports HTTP, HTTPS, SOCKS5, SOCKS5H proxies (socks5h:// resolves DNS at the proxy)
# Global fallback proxy — used only when an account has no dedicated proxy
# (per-account proxy can be set via the dashboard or ACCOUNTS env var)
# Priority: account.proxy > PROXY_URL > no proxy
Expand Down
6 changes: 3 additions & 3 deletions README-en.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Each account can be configured with its own outbound proxy, allowing multiple ac

**Priority:** `account.proxy` > Global `PROXY_URL` > No proxy

**Supported Proxy Protocols:** HTTP / HTTPS / SOCKS5 (consistent with `PROXY_URL`)
**Supported Proxy Protocols:** HTTP / HTTPS / SOCKS5 / SOCKS5H (consistent with `PROXY_URL`)

**Frontend Configuration (Recommended):**
Open the management panel → Fill in the "Proxy Address" field when adding accounts, or click the "Modify Proxy" button on existing account cards.
Expand Down Expand Up @@ -103,7 +103,7 @@ AGENT_TURN_MAX_TOOL_CALLS=24 # Anthropic path: text-channel tool_use cap per ag
# 🌐 Proxy and Reverse Proxy Configuration
QWEN_CHAT_PROXY_URL= # Custom Chat API reverse proxy URL (default: https://chat.qwen.ai)
QWEN_CLI_PROXY_URL= # Custom CLI API reverse proxy URL (default: https://portal.qwen.ai)
PROXY_URL= # HTTP/HTTPS/SOCKS5 proxy address (example: http://127.0.0.1:7890)
PROXY_URL= # HTTP/HTTPS/SOCKS5/SOCKS5H proxy address (example: http://127.0.0.1:7890)

# 🗄️ Data Storage
DATA_SAVE_MODE=none # Data save mode (none/file/redis)
Expand Down Expand Up @@ -132,7 +132,7 @@ CACHE_MODE=default # Image cache mode (default/file)
| `AGENT_CONTEXT_LIVE_PROMPT_BYTES` | Maximum size of the tool protocol and current turn kept in the live request after context externalization | `49152` (48 KiB) |
| `QWEN_CHAT_PROXY_URL` | Custom Chat API reverse proxy address | `https://your-proxy.com` |
| `QWEN_CLI_PROXY_URL` | Custom CLI API reverse proxy address | `https://your-cli-proxy.com` |
| `PROXY_URL` | Outbound request proxy address, supports HTTP/HTTPS/SOCKS5 | `http://127.0.0.1:7890` |
| `PROXY_URL` | Outbound request proxy address, supports HTTP/HTTPS/SOCKS5/SOCKS5H | `http://127.0.0.1:7890` |
| `DATA_SAVE_MODE` | Data persistence method | `none`/`file`/[redis](file://d:\Code\Qwen2API\src\utils\logger.js#L294-L296) |
| `REDIS_URL` | Redis database connection address, use `rediss://` protocol when using TLS encryption | `redis://localhost:6379` or `rediss://xxx.upstash.io` |
| `BATCH_LOGIN_CONCURRENCY` | Login concurrency during batch account addition, can be adjusted dynamically in frontend system settings | `5` |
Expand Down
6 changes: 3 additions & 3 deletions README-ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ QWEN_CHAT_PROXY_URL=http://127.0.0.1:8000/qwen # Адрес обратного

**Приоритет:** `account.proxy` > глобальный `PROXY_URL` > без прокси

**Поддерживаемые протоколы:** HTTP / HTTPS / SOCKS5 (как у `PROXY_URL`)
**Поддерживаемые протоколы:** HTTP / HTTPS / SOCKS5 / SOCKS5H (как у `PROXY_URL`)

**Через панель (рекомендуется):**
Откройте dashboard → при добавлении аккаунта заполните поле «Proxy URL», либо нажмите кнопку «Изменить proxy» на карточке существующего аккаунта.
Expand Down Expand Up @@ -123,7 +123,7 @@ AGENT_TURN_MAX_TOOL_CALLS=24 # Anthropic: лимит tool_use по тексто
# 🌐 Прокси и обратный прокси
QWEN_CHAT_PROXY_URL= # Пользовательский URL обратного прокси Chat API (по умолчанию: https://chat.qwen.ai)
QWEN_CLI_PROXY_URL= # Пользовательский URL обратного прокси CLI API (по умолчанию: https://portal.qwen.ai)
PROXY_URL= # Адрес HTTP/HTTPS/SOCKS5 прокси (например: http://127.0.0.1:7890)
PROXY_URL= # Адрес HTTP/HTTPS/SOCKS5/SOCKS5H прокси (например: http://127.0.0.1:7890)

# 🗄️ Хранение данных
DATA_SAVE_MODE=none # Режим сохранения данных (none/file/redis)
Expand All @@ -150,7 +150,7 @@ CACHE_MODE=default # Режим кэширования изображ
| `AGENT_TURN_MAX_TOOL_CALLS` | Путь Anthropic: лимит блоков `tool_use` по текстовому каналу за один ход агента (4–256). Если модель «идёт вразнос» после нарративного `[TOOL CALL]` (сотни повторов одного вызова, выдуманная сессия целиком), upstream обрывается сразу после N-го принятого вызова, а принятые вызовы отдаются со `stop_reason=tool_use`; после уже принятого в более раннем delta вызова дубликат, отклонённый вызов или проза/размышление также обрывают ход | `24` |
| `QWEN_CHAT_PROXY_URL` | Пользовательский адрес обратного прокси Chat API | `https://your-proxy.com` |
| `QWEN_CLI_PROXY_URL` | Пользовательский адрес обратного прокси CLI API | `https://your-cli-proxy.com` |
| `PROXY_URL` | Адрес прокси для исходящих запросов, поддержка HTTP/HTTPS/SOCKS5 | `http://127.0.0.1:7890` |
| `PROXY_URL` | Адрес прокси для исходящих запросов, поддержка HTTP/HTTPS/SOCKS5/SOCKS5H | `http://127.0.0.1:7890` |
| `DATA_SAVE_MODE` | Способ персистентного хранения данных | `none`/`file`/`redis` |
| `REDIS_URL` | Адрес подключения к Redis, при использовании TLS-шифрования необходим протокол `rediss://` | `redis://localhost:6379` или `rediss://xxx.upstash.io` |
| `BATCH_LOGIN_CONCURRENCY` | Параллельность входа при массовом добавлении аккаунтов, можно динамически менять в системных настройках фронтенда | `5` |
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Qwen-Proxy 是一个将 `https://chat.qwen.ai` 和 `Qwen Code / Qwen Cli` 转换

**优先级:** `account.proxy` > 全局 `PROXY_URL` > 不使用代理

**支持的代理协议:** HTTP / HTTPS / SOCKS5(与 `PROXY_URL` 一致)
**支持的代理协议:** HTTP / HTTPS / SOCKS5 / SOCKS5H(与 `PROXY_URL` 一致)

**前端配置(推荐):**
打开管理面板 → 添加账号时填写 "代理地址" 字段,或在已有账号卡片上点击 "修改代理" 按钮。
Expand Down Expand Up @@ -108,7 +108,7 @@ AGENT_CONTEXT_LIVE_PROMPT_BYTES=49152 # 外置后仍内联保留的关键任
# 🌐 代理与反代配置
QWEN_CHAT_PROXY_URL= # 自定义 Chat API 反代URL (默认: https://chat.qwen.ai)
QWEN_CLI_PROXY_URL= # 自定义 CLI API 反代URL (默认: https://portal.qwen.ai)
PROXY_URL= # HTTP/HTTPS/SOCKS5 代理地址 (例如: http://127.0.0.1:7890)
PROXY_URL= # HTTP/HTTPS/SOCKS5/SOCKS5H 代理地址 (例如: http://127.0.0.1:7890)

# 🗄️ 数据存储
DATA_SAVE_MODE=none # 数据保存模式 (none/file/redis)
Expand Down Expand Up @@ -140,7 +140,7 @@ CACHE_MODE=default # 图片缓存模式 (default/file)
| `AGENT_CONTEXT_LIVE_PROMPT_BYTES` | 上下文外置后,实时请求中保留的工具协议、system/developer 指令、原始任务、最近工具进度和当前结果的最大大小 | `49152`(48 KiB) |
| `QWEN_CHAT_PROXY_URL` | 自定义 Chat API 反代地址 | `https://your-proxy.com` |
| `QWEN_CLI_PROXY_URL` | 自定义 CLI API 反代地址 | `https://your-cli-proxy.com` |
| `PROXY_URL` | 出站请求代理地址,支持 HTTP/HTTPS/SOCKS5 | `http://127.0.0.1:7890` |
| `PROXY_URL` | 出站请求代理地址,支持 HTTP/HTTPS/SOCKS5/SOCKS5H | `http://127.0.0.1:7890` |
| `DATA_SAVE_MODE` | 数据持久化方式 | `none`/`file`/`redis` |
| `REDIS_URL` | Redis 数据库连接地址,使用TLS加密时需使用 `rediss://` 协议 | `redis://localhost:6379` 或 `rediss://xxx.upstash.io` |
| `BATCH_LOGIN_CONCURRENCY` | 批量添加账号时的登录并发数,可在前端系统设置中动态调整 | `5` |
Expand Down
2 changes: 1 addition & 1 deletion public/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
"forceRefreshAllConfirm": "Are you sure you want to force refresh the tokens of all accounts? \nThis will refresh all accounts, regardless of whether they are about to expire, and may take a long time.",
"forceRefreshAllFailed": "Forced refresh failed:",
"progressRetry": "Progress query failed, retrying...",
"proxyInvalid": "Format error: must start with http://, https:// or socks5://",
"proxyInvalid": "Format error: must start with http://, https://, socks5:// or socks5h://",
"proxyUpdateFailed": "Agent update failed:",
"proxyUpdateSuccess": "Agent updated successfully",
"refreshAllComplete": "Batch refresh completed, {n} accounts were successfully refreshed",
Expand Down
2 changes: 1 addition & 1 deletion public/src/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"forceRefreshAllFailed": "Ошибка принудительного обновления: ",
"proxyUpdateSuccess": "Proxy обновлён",
"proxyUpdateFailed": "Ошибка обновления proxy: ",
"proxyInvalid": "Неверный формат: ожидается http://, https:// или socks5://"
"proxyInvalid": "Неверный формат: ожидается http://, https://, socks5:// или socks5h://"
},
"settings": {
"title": "Настройки",
Expand Down
2 changes: 1 addition & 1 deletion public/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
"forceRefreshAllFailed": "强制刷新失败: ",
"proxyUpdateSuccess": "代理更新成功",
"proxyUpdateFailed": "代理更新失败: ",
"proxyInvalid": "格式错误:必须以 http://、https:// 或 socks5:// 开头"
"proxyInvalid": "格式错误:必须以 http://、https://、socks5://socks5h:// 开头"
},
"settings": {
"title": "系统设置",
Expand Down
2 changes: 1 addition & 1 deletion public/src/views/dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ const editProxy = ref({ email: '', proxy: '' })
const isSavingProxy = ref(false)

// 与后端 src/utils/proxy-helper.js#PROXY_URL_REGEX 保持一致
const PROXY_URL_REGEX = /^(https?|socks5):\/\/[^\s]+$/i
const PROXY_URL_REGEX = /^(https?|socks5h?):\/\/[^\s]+$/i
const isValidProxy = (value) => {
if (!value) return true
const trimmed = String(value).trim()
Expand Down
23 changes: 18 additions & 5 deletions src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ dotenv.config()
* 解析API_KEY环境变量,支持逗号分隔的多个key
* @returns {Object} 包含apiKeys数组和adminKey的对象
*/
const parseApiKeys = () => {
const apiKeyEnv = process.env.API_KEY
const parseApiKeys = (apiKeyEnv = process.env.API_KEY) => {
if (!apiKeyEnv) {
return { apiKeys: [], adminKey: null }
}

const keys = apiKeyEnv.split(',').map(key => key.trim()).filter(key => key.length > 0)
const slots = apiKeyEnv.split(',').map(key => key.trim())
// 空槽位 = 变量没展开(`API_KEY=${A},${B}` 里 A 未定义就变成 `,B`)。旧代码把空槽
// 过滤掉照常启动:admin key 静默变成了后面那个客户端 key,dashboard 用真正的 admin
// key 反而报 "invalid"。2026-09-16 qwen-next 就是这样丢掉后台访问的。没人会故意写
// 出空槽,所以这里直接拒绝启动,而不是带着残缺的密钥表跑下去。
const emptyAt = slots.map((key, index) => (key.length === 0 ? index + 1 : 0)).filter(Boolean)
if (emptyAt.length > 0) {
throw new Error(
`API_KEY 有 ${emptyAt.length} 个空槽位(第 ${emptyAt.join('、')} 位,共 ${slots.length} 位)——` +
'多半是环境变量没有展开。请检查部署时是否注入了全部密钥(例如 compose 的 ${...} 是否有值)。'
)
}
return {
apiKeys: keys,
adminKey: keys.length > 0 ? keys[0] : null
apiKeys: slots,
adminKey: slots.length > 0 ? slots[0] : null
}
}

Expand Down Expand Up @@ -137,4 +147,7 @@ const config = {
)
}

// 暴露解析器本身以便测试空槽位的拒绝逻辑(config 对象是模块级单例,无法重复解析)。
config.parseApiKeys = parseApiKeys

module.exports = config
Loading