mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
fix: 修复 OpenAI 账户代理配置保存问题
- 修复 OAuth 方式添加 OpenAI 账户时缺少 priority 字段 - 修复创建 OpenAI 账户时错误检查 proxy.enabled 导致代理未保存 - 添加 OpenAI OAuth token 交换时的 SOCKS5 代理支持 - 添加 OpenAI API 请求转发时的代理支持 - 参考 Claude/Gemini 实现统一代理处理逻辑
This commit is contained in:
@@ -18,6 +18,8 @@ const crypto = require('crypto')
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const config = require('../../config/config')
|
const config = require('../../config/config')
|
||||||
|
const { SocksProxyAgent } = require('socks-proxy-agent')
|
||||||
|
const { HttpsProxyAgent } = require('https-proxy-agent')
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
@@ -4501,12 +4503,16 @@ router.post('/openai-accounts/exchange-code', authenticateAdmin, async (req, res
|
|||||||
|
|
||||||
if (sessionData.proxy) {
|
if (sessionData.proxy) {
|
||||||
const { type, host, port, username, password } = sessionData.proxy
|
const { type, host, port, username, password } = sessionData.proxy
|
||||||
if (type === 'http' || type === 'https') {
|
if (type === 'socks5') {
|
||||||
axiosConfig.proxy = {
|
// SOCKS5 代理
|
||||||
host,
|
const auth = username && password ? `${username}:${password}@` : ''
|
||||||
port: parseInt(port),
|
const socksUrl = `socks5://${auth}${host}:${port}`
|
||||||
auth: username && password ? { username, password } : undefined
|
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,
|
rateLimitDuration !== undefined && rateLimitDuration !== null ? rateLimitDuration : 60,
|
||||||
openaiOauth: openaiOauth || {},
|
openaiOauth: openaiOauth || {},
|
||||||
accountInfo: accountInfo || {},
|
accountInfo: accountInfo || {},
|
||||||
proxy: proxy?.enabled
|
proxy: proxy || null,
|
||||||
? {
|
|
||||||
type: proxy.type,
|
|
||||||
host: proxy.host,
|
|
||||||
port: proxy.port,
|
|
||||||
username: proxy.username || null,
|
|
||||||
password: proxy.password || null
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
isActive: true,
|
isActive: true,
|
||||||
schedulable: true
|
schedulable: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,31 @@ const unifiedOpenAIScheduler = require('../services/unifiedOpenAIScheduler')
|
|||||||
const openaiAccountService = require('../services/openaiAccountService')
|
const openaiAccountService = require('../services/openaiAccountService')
|
||||||
const apiKeyService = require('../services/apiKeyService')
|
const apiKeyService = require('../services/apiKeyService')
|
||||||
const crypto = require('crypto')
|
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 账户
|
// 使用统一调度器选择 OpenAI 账户
|
||||||
async function getOpenAIAuthToken(apiKeyData, sessionId = null, requestedModel = null) {
|
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')
|
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})`)
|
logger.info(`Selected OpenAI account: ${account.name} (${result.accountId})`)
|
||||||
return {
|
return {
|
||||||
accessToken,
|
accessToken,
|
||||||
accountId: result.accountId,
|
accountId: result.accountId,
|
||||||
accountName: account.name
|
accountName: account.name,
|
||||||
|
proxy
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to get OpenAI auth token:', 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,
|
apiKeyData,
|
||||||
sessionId,
|
sessionId,
|
||||||
requestedModel
|
requestedModel
|
||||||
@@ -133,22 +169,36 @@ router.post('/responses', authenticateApiKey, async (req, res) => {
|
|||||||
headers['content-type'] = 'application/json'
|
headers['content-type'] = 'application/json'
|
||||||
req.body['store'] = false
|
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 参数决定请求类型
|
// 根据 stream 参数决定请求类型
|
||||||
if (isStream) {
|
if (isStream) {
|
||||||
// 流式请求
|
// 流式请求
|
||||||
upstream = await axios.post('https://chatgpt.com/backend-api/codex/responses', req.body, {
|
upstream = await axios.post('https://chatgpt.com/backend-api/codex/responses', req.body, {
|
||||||
headers,
|
...axiosConfig,
|
||||||
responseType: 'stream',
|
responseType: 'stream'
|
||||||
timeout: 60000,
|
|
||||||
validateStatus: () => true
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 非流式请求
|
// 非流式请求
|
||||||
upstream = await axios.post('https://chatgpt.com/backend-api/codex/responses', req.body, {
|
upstream = await axios.post(
|
||||||
headers,
|
'https://chatgpt.com/backend-api/codex/responses',
|
||||||
timeout: 60000,
|
req.body,
|
||||||
validateStatus: () => true
|
axiosConfig
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
res.status(upstream.status)
|
res.status(upstream.status)
|
||||||
|
|
||||||
|
|||||||
@@ -1687,6 +1687,7 @@ const handleOAuthSuccess = async (tokenInfo) => {
|
|||||||
} else if (form.value.platform === 'openai') {
|
} else if (form.value.platform === 'openai') {
|
||||||
data.openaiOauth = tokenInfo.tokens || tokenInfo
|
data.openaiOauth = tokenInfo.tokens || tokenInfo
|
||||||
data.accountInfo = tokenInfo.accountInfo
|
data.accountInfo = tokenInfo.accountInfo
|
||||||
|
data.priority = form.value.priority || 50
|
||||||
}
|
}
|
||||||
|
|
||||||
let result
|
let result
|
||||||
|
|||||||
Reference in New Issue
Block a user