From 5428462f557b56690454af01a7696ff104c51084 Mon Sep 17 00:00:00 2001 From: shaw Date: Wed, 13 Aug 2025 22:44:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20OpenAI=20=E8=B4=A6?= =?UTF-8?q?=E6=88=B7=E4=BB=A3=E7=90=86=E9=85=8D=E7=BD=AE=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 OAuth 方式添加 OpenAI 账户时缺少 priority 字段 - 修复创建 OpenAI 账户时错误检查 proxy.enabled 导致代理未保存 - 添加 OpenAI OAuth token 交换时的 SOCKS5 代理支持 - 添加 OpenAI API 请求转发时的代理支持 - 参考 Claude/Gemini 实现统一代理处理逻辑 --- src/routes/admin.js | 28 ++++---- src/routes/openaiRoutes.js | 72 ++++++++++++++++--- .../src/components/accounts/AccountForm.vue | 1 + 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/routes/admin.js b/src/routes/admin.js index 9c85bfbf..997bef0b 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -18,6 +18,8 @@ const crypto = require('crypto') const fs = require('fs') const path = require('path') const config = require('../../config/config') +const { SocksProxyAgent } = require('socks-proxy-agent') +const { HttpsProxyAgent } = require('https-proxy-agent') const router = express.Router() @@ -4501,12 +4503,16 @@ router.post('/openai-accounts/exchange-code', authenticateAdmin, async (req, res if (sessionData.proxy) { const { type, host, port, username, password } = sessionData.proxy - if (type === 'http' || type === 'https') { - axiosConfig.proxy = { - host, - port: parseInt(port), - auth: username && password ? { username, password } : undefined - } + if (type === 'socks5') { + // SOCKS5 代理 + const auth = username && password ? `${username}:${password}@` : '' + const socksUrl = `socks5://${auth}${host}:${port}` + axiosConfig.httpsAgent = new SocksProxyAgent(socksUrl) + } else if (type === 'http' || type === 'https') { + // HTTP/HTTPS 代理 + const auth = username && password ? `${username}:${password}@` : '' + const proxyUrl = `${type}://${auth}${host}:${port}` + axiosConfig.httpsAgent = new HttpsProxyAgent(proxyUrl) } } @@ -4678,15 +4684,7 @@ router.post('/openai-accounts', authenticateAdmin, async (req, res) => { rateLimitDuration !== undefined && rateLimitDuration !== null ? rateLimitDuration : 60, openaiOauth: openaiOauth || {}, accountInfo: accountInfo || {}, - proxy: proxy?.enabled - ? { - type: proxy.type, - host: proxy.host, - port: proxy.port, - username: proxy.username || null, - password: proxy.password || null - } - : null, + proxy: proxy || null, isActive: true, schedulable: true } diff --git a/src/routes/openaiRoutes.js b/src/routes/openaiRoutes.js index af1a482a..2679da6f 100644 --- a/src/routes/openaiRoutes.js +++ b/src/routes/openaiRoutes.js @@ -8,6 +8,31 @@ const unifiedOpenAIScheduler = require('../services/unifiedOpenAIScheduler') const openaiAccountService = require('../services/openaiAccountService') const apiKeyService = require('../services/apiKeyService') const crypto = require('crypto') +const { SocksProxyAgent } = require('socks-proxy-agent') +const { HttpsProxyAgent } = require('https-proxy-agent') + +// 创建代理 Agent +function createProxyAgent(proxy) { + if (!proxy) { + return null + } + + try { + if (proxy.type === 'socks5') { + const auth = proxy.username && proxy.password ? `${proxy.username}:${proxy.password}@` : '' + const socksUrl = `socks5://${auth}${proxy.host}:${proxy.port}` + return new SocksProxyAgent(socksUrl) + } else if (proxy.type === 'http' || proxy.type === 'https') { + const auth = proxy.username && proxy.password ? `${proxy.username}:${proxy.password}@` : '' + const proxyUrl = `${proxy.type}://${auth}${proxy.host}:${proxy.port}` + return new HttpsProxyAgent(proxyUrl) + } + } catch (error) { + logger.warn('Failed to create proxy agent:', error) + } + + return null +} // 使用统一调度器选择 OpenAI 账户 async function getOpenAIAuthToken(apiKeyData, sessionId = null, requestedModel = null) { @@ -40,11 +65,22 @@ async function getOpenAIAuthToken(apiKeyData, sessionId = null, requestedModel = throw new Error('Failed to decrypt OpenAI accessToken') } + // 解析代理配置 + let proxy = null + if (account.proxy) { + try { + proxy = typeof account.proxy === 'string' ? JSON.parse(account.proxy) : account.proxy + } catch (e) { + logger.warn('Failed to parse proxy configuration:', e) + } + } + logger.info(`Selected OpenAI account: ${account.name} (${result.accountId})`) return { accessToken, accountId: result.accountId, - accountName: account.name + accountName: account.name, + proxy } } catch (error) { logger.error('Failed to get OpenAI auth token:', error) @@ -108,7 +144,7 @@ router.post('/responses', authenticateApiKey, async (req, res) => { } // 使用调度器选择账户 - const { accessToken, accountId } = await getOpenAIAuthToken( + const { accessToken, accountId, proxy } = await getOpenAIAuthToken( apiKeyData, sessionId, requestedModel @@ -133,22 +169,36 @@ router.post('/responses', authenticateApiKey, async (req, res) => { headers['content-type'] = 'application/json' req.body['store'] = false + // 创建代理 agent + const proxyAgent = createProxyAgent(proxy) + + // 配置请求选项 + const axiosConfig = { + headers, + timeout: 60000, + validateStatus: () => true + } + + // 如果有代理,添加代理配置 + if (proxyAgent) { + axiosConfig.httpsAgent = proxyAgent + logger.info('Using proxy for OpenAI request') + } + // 根据 stream 参数决定请求类型 if (isStream) { // 流式请求 upstream = await axios.post('https://chatgpt.com/backend-api/codex/responses', req.body, { - headers, - responseType: 'stream', - timeout: 60000, - validateStatus: () => true + ...axiosConfig, + responseType: 'stream' }) } else { // 非流式请求 - upstream = await axios.post('https://chatgpt.com/backend-api/codex/responses', req.body, { - headers, - timeout: 60000, - validateStatus: () => true - }) + upstream = await axios.post( + 'https://chatgpt.com/backend-api/codex/responses', + req.body, + axiosConfig + ) } res.status(upstream.status) diff --git a/web/admin-spa/src/components/accounts/AccountForm.vue b/web/admin-spa/src/components/accounts/AccountForm.vue index 2618dd1f..14f1a215 100644 --- a/web/admin-spa/src/components/accounts/AccountForm.vue +++ b/web/admin-spa/src/components/accounts/AccountForm.vue @@ -1687,6 +1687,7 @@ const handleOAuthSuccess = async (tokenInfo) => { } else if (form.value.platform === 'openai') { data.openaiOauth = tokenInfo.tokens || tokenInfo data.accountInfo = tokenInfo.accountInfo + data.priority = form.value.priority || 50 } let result