import express from 'express'; import fetch from 'node-fetch'; import dotenv from 'dotenv'; import { v4 as uuidv4 } from 'uuid'; import cors from 'cors'; // 初始化环境变量 dotenv.config(); /** * 配置管理类 */ class Config { constructor() { this.initializeApiKeys(); this.initializeAuth(); // 已使用的API Key池 this.usedApiKeys = []; // 失效的API Key池 this.invalidApiKeys = []; // Gemini安全设置 this.geminiSafety = [ { category: 'HARM_CATEGORY_HARASSMENT', threshold: 'OFF', }, { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'OFF', }, { category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', threshold: 'OFF', }, { category: 'HARM_CATEGORY_DANGEROUS_CONTENT', threshold: 'OFF', }, { category: 'HARM_CATEGORY_CIVIC_INTEGRITY', threshold: 'OFF', }, ]; } /** * 初始化API Keys */ initializeApiKeys() { const apiKeysEnv = process.env.GEMINI_API_KEYS; if (!apiKeysEnv) { console.error('❌ 错误: 未找到 GEMINI_API_KEYS 环境变量'); process.exit(1); } // 通过换行符分割API Keys this.apiKeys = apiKeysEnv .split('\n') .map(key => key.trim()) .filter(key => key.length > 0); if (this.apiKeys.length === 0) { console.error('❌ 错误: 没有找到有效的API Keys'); process.exit(1); } console.log(`✅ 成功加载 ${this.apiKeys.length} 个API Keys`); } /** * 初始化认证配置 */ initializeAuth() { this.authToken = process.env.AUTH_TOKEN || 'sk-123456'; } /** * 获取可用的API Key(负载均衡) */ getApiKey() { if (this.apiKeys.length === 0) { if (this.usedApiKeys.length > 0) { this.apiKeys.push(...this.usedApiKeys); this.usedApiKeys = []; } else { return null; } } const apiKey = this.apiKeys.shift(); this.usedApiKeys.push(apiKey); return apiKey; } /** * 获取第一个可用的API Key(用于模型列表请求) */ getFirstAvailableApiKey() { if (this.apiKeys.length > 0) { return this.apiKeys[0]; } if (this.usedApiKeys.length > 0) { return this.usedApiKeys[0]; } return null; } /** * 将API Key标记为失效 */ markKeyAsInvalid(apiKey) { const usedIndex = this.usedApiKeys.indexOf(apiKey); if (usedIndex !== -1) { this.usedApiKeys.splice(usedIndex, 1); } const mainIndex = this.apiKeys.indexOf(apiKey); if (mainIndex !== -1) { this.apiKeys.splice(mainIndex, 1); } if (!this.invalidApiKeys.includes(apiKey)) { this.invalidApiKeys.push(apiKey); } console.warn(`⚠️ API Key 已标记为失效: ${apiKey.substring(0, 10)}...`); } /** * 将API Key移回已使用池 */ moveToUsed(apiKey) { if (!this.usedApiKeys.includes(apiKey)) { this.usedApiKeys.push(apiKey); } } /** * 验证授权头 */ validateAuth(authHeader) { if (!authHeader) { return false; } const token = authHeader.replace('Bearer ', ''); return token === this.authToken; } } /** * 图片处理器类 */ class ImageProcessor { /** * 从data URL中提取MIME类型和base64数据 */ static parseDataUrl(dataUrl) { try { // 匹配data:image/jpeg;base64,格式 const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/); if (!match) { throw new Error('无效的data URL格式'); } const mimeType = match[1]; const base64Data = match[2]; // 验证MIME类型是否为支持的图片格式 const supportedMimeTypes = [ 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/bmp', 'image/tiff' ]; if (!supportedMimeTypes.includes(mimeType.toLowerCase())) { throw new Error(`不支持的图片格式: ${mimeType}`); } return { mimeType, data: base64Data }; } catch (error) { console.error('解析图片data URL错误:', error); throw error; } } /** * 验证base64数据是否有效 */ static validateBase64(base64String) { try { // 基本格式检查 if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64String)) { return false; } // 长度检查(base64编码长度应该是4的倍数) return base64String.length % 4 === 0; } catch (error) { return false; } } /** * 从URL下载图片并转换为base64 */ static async fetchImageAsBase64(imageUrl) { try { const response = await fetch(imageUrl, { timeout: 30000, // 30秒超时 headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); if (!response.ok) { throw new Error(`获取图片失败: HTTP ${response.status}`); } const contentType = response.headers.get('content-type'); if (!contentType || !contentType.startsWith('image/')) { throw new Error(`URL返回的不是图片类型: ${contentType}`); } const buffer = await response.buffer(); const base64Data = buffer.toString('base64'); return { mimeType: contentType, data: base64Data }; } catch (error) { console.error('下载图片错误:', error); throw error; } } } /** * 消息转换器类(增强版) */ class MessageConverter { /** * 将OpenAI格式的消息转换为Gemini格式(支持图片) */ static async convertMessages(openaiMessages) { const geminiMessages = []; let currentRole = null; let currentParts = []; for (const message of openaiMessages) { let role = message.role; let content = message.content; // 角色转换 if (role === 'system') { role = 'user'; } if (role === 'assistant') { role = 'model'; } // 处理内容 let parts = []; if (typeof content === 'string') { // 简单文本消息 parts = [{ text: content }]; } else if (Array.isArray(content)) { // 多模态消息(包含文本和图片) parts = await this.convertContentArray(content); } else { // 其他格式,转为文本 parts = [{ text: String(content) }]; } // 合并相同角色的连续消息 if (role === currentRole) { currentParts.push(...parts); } else { // 保存上一个角色的消息 if (currentRole !== null && currentParts.length > 0) { geminiMessages.push({ role: currentRole, parts: currentParts }); } // 开始新角色 currentRole = role; currentParts = [...parts]; } } // 添加最后一个消息 if (currentRole !== null && currentParts.length > 0) { geminiMessages.push({ role: currentRole, parts: currentParts }); } return geminiMessages; } /** * 转换OpenAI的content数组为Gemini的parts格式 */ static async convertContentArray(contentArray) { const parts = []; for (const item of contentArray) { try { if (item.type === 'text') { // 文本内容 parts.push({ text: item.text || '' }); } else if (item.type === 'image_url') { // 图片内容 const imagePart = await this.convertImageContent(item); if (imagePart) { parts.push(imagePart); } } else { // 其他类型,尝试转为文本 console.warn(`未知的内容类型: ${item.type},将转为文本处理`); parts.push({ text: JSON.stringify(item) }); } } catch (error) { console.error('转换内容项错误:', error); // 出错时跳过该项,避免整个请求失败 continue; } } return parts; } /** * 转换图片内容为Gemini格式 */ static async convertImageContent(imageItem) { try { const imageUrl = imageItem.image_url?.url; if (!imageUrl) { throw new Error('缺少图片URL'); } let imageData; if (imageUrl.startsWith('data:')) { // 处理base64数据URL imageData = ImageProcessor.parseDataUrl(imageUrl); // 验证base64数据 if (!ImageProcessor.validateBase64(imageData.data)) { throw new Error('无效的base64图片数据'); } } else if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) { // 处理网络图片URL imageData = await ImageProcessor.fetchImageAsBase64(imageUrl); } else { throw new Error(`不支持的图片URL格式: ${imageUrl}`); } // 返回Gemini格式的图片数据 return { inlineData: { mimeType: imageData.mimeType, data: imageData.data } }; } catch (error) { console.error('转换图片内容错误:', error); // 返回错误信息文本,而不是抛出异常 return { text: `[图片处理失败: ${error.message}]` }; } } /** * 从OpenAI请求中提取参数 */ static extractParams(openaiRequest) { return { model: openaiRequest.model || 'gemini-1.5-flash', messages: openaiRequest.messages || [], stream: openaiRequest.stream || false, temperature: openaiRequest.temperature, maxTokens: openaiRequest.max_tokens, topP: openaiRequest.top_p }; } } /** * 模型管理类 */ class ModelManager { constructor(config) { this.config = config; this.cachedModels = null; this.cacheExpiry = null; this.cacheTimeout = 5 * 60 * 1000; // 5分钟缓存 } /** * 获取模型列表 */ async getModels() { if (this.cachedModels && this.cacheExpiry && Date.now() < this.cacheExpiry) { return { success: true, data: this.cachedModels }; } const apiKey = this.config.getFirstAvailableApiKey(); if (!apiKey) { return { success: false, error: '没有可用的API Key', status: 503 }; } try { const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { return { success: false, error: `获取模型列表失败: ${response.status}`, status: response.status }; } const geminiResponse = await response.json(); const filteredModels = this.filterModels(geminiResponse.models || []); this.cachedModels = filteredModels; this.cacheExpiry = Date.now() + this.cacheTimeout; return { success: true, data: filteredModels }; } catch (error) { console.error('获取模型列表错误:', error); return { success: false, error: '网络请求失败', status: 500 }; } } /** * 过滤模型 - 保持原始Gemini模型名,并增加视觉模型支持 */ filterModels(models) { const allowedPrefixes = [ 'models/gemini-2.5-flash', 'models/gemini-2.0-flash', 'models/gemini-1.5-flash' ]; const excludedModels = [ 'models/gemini-1.5-flash-8b' ]; const filteredModels = models.filter(model => { const modelName = model.name; if (excludedModels.some(excluded => modelName.startsWith(excluded))) { return false; } if(modelName == "models/gemini-2.5-pro"){ return true; } return allowedPrefixes.some(prefix => modelName.startsWith(prefix)); }); // 转换为OpenAI格式但保持Gemini模型名 const processedModels = filteredModels.map(model => { const modelId = model.name.replace('models/', ''); return { id: modelId, // 直接使用Gemini模型名 object: 'model', created: Math.floor(Date.now() / 1000), owned_by: 'google', permission: [ { id: `modelperm-${modelId}`, object: 'model_permission', created: Math.floor(Date.now() / 1000), allow_create_engine: false, allow_sampling: true, allow_logprobs: false, allow_search_indices: false, allow_view: true, allow_fine_tuning: false, organization: '*', group: null, is_blocking: false } ], root: modelId, parent: null }; }); return { object: 'list', data: processedModels }; } } /** * Gemini API 请求构建器类 */ class GeminiRequestBuilder { constructor(config) { this.config = config; } /** * 构建Gemini API请求体 */ buildRequestBody(geminiMessages, params) { const requestBody = { contents: geminiMessages, safetySettings: this.config.geminiSafety, generationConfig: {} }; if (params.temperature !== undefined) { requestBody.generationConfig.temperature = params.temperature; } if (params.maxTokens !== undefined) { requestBody.generationConfig.maxOutputTokens = params.maxTokens; } if (params.topP !== undefined) { requestBody.generationConfig.topP = params.topP; } return requestBody; } /** * 构建Gemini API URL */ buildApiUrl(model, apiKey, isStream = false) { const method = isStream ? 'streamGenerateContent' : 'generateContent'; return `https://generativelanguage.googleapis.com/v1beta/models/${model}:${method}?key=${apiKey}`; } } /** * 响应转换器类 */ class ResponseConverter { /** * 将Gemini流式响应块转换为OpenAI格式 */ static convertStreamChunk(geminiData, requestId, model) { try { if (geminiData.candidates && geminiData.candidates[0]) { const candidate = geminiData.candidates[0]; if (candidate.content && candidate.content.parts) { const text = candidate.content.parts[0]?.text || ''; const openaiChunk = { id: requestId, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: model, // 使用当前请求的模型名 choices: [{ index: 0, delta: { content: text }, finish_reason: candidate.finishReason === 'STOP' ? 'stop' : null }] }; return `data: ${JSON.stringify(openaiChunk)}\n\n`; } } return ''; } catch (error) { console.error('转换流响应块错误:', error); return ''; } } /** * 将Gemini非流式响应转换为OpenAI格式 */ static convertNormalResponse(geminiResponse, requestId, model) { const openaiResponse = { id: requestId, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model: model, // 使用当前请求的模型名 choices: [], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } }; if (geminiResponse.candidates && geminiResponse.candidates[0]) { const candidate = geminiResponse.candidates[0]; if (candidate.content && candidate.content.parts) { const text = candidate.content.parts.map(part => part.text).join(''); openaiResponse.choices.push({ index: 0, message: { role: 'assistant', content: text }, finish_reason: candidate.finishReason === 'STOP' ? 'stop' : 'length' }); } } // 尝试从usage信息中获取token使用量 if (geminiResponse.usageMetadata) { openaiResponse.usage = { prompt_tokens: geminiResponse.usageMetadata.promptTokenCount || 0, completion_tokens: geminiResponse.usageMetadata.candidatesTokenCount || 0, total_tokens: geminiResponse.usageMetadata.totalTokenCount || 0 }; } return openaiResponse; } /** * 将文本拆分为假流式块 */ static splitTextToFakeStream(text, requestId, model) { const chunks = []; const chunkSize = 3; // 每个块包含的字符数 for (let i = 0; i < text.length; i += chunkSize) { const chunk = text.slice(i, i + chunkSize); const isLast = i + chunkSize >= text.length; const openaiChunk = { id: requestId, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: model, choices: [{ index: 0, delta: { content: chunk }, finish_reason: isLast ? 'stop' : null }] }; chunks.push(`data: ${JSON.stringify(openaiChunk)}\n\n`); } return chunks; } } /** * Gemini实时流式响应解析器 */ class GeminiRealtimeStreamParser { constructor(response, onChunk) { this.response = response; this.onChunk = onChunk; this.buffer = ''; this.bufferLv = 0; this.inString = false; this.escapeNext = false; this.decoder = new TextDecoder(); } async start() { try { for await (const chunk of this.response.body) { const text = this.decoder.decode(chunk, { stream: true }); await this.processText(text); } await this.handleRemainingBuffer(); } catch (error) { console.error('流式解析错误:', error); throw error; } } async processText(text) { for (const char of text) { if (this.escapeNext) { if (this.bufferLv > 1) { this.buffer += char; } this.escapeNext = false; continue; } if (char === '\\' && this.inString) { this.escapeNext = true; if (this.bufferLv > 1) { this.buffer += char; } continue; } if (char === '"') { this.inString = !this.inString; } if (!this.inString) { if (char === '{' || char === '[') { this.bufferLv++; } else if (char === '}' || char === ']') { this.bufferLv--; } } if (this.bufferLv > 1) { if (this.inString && char === '\n') { this.buffer += '\\n'; } else { this.buffer += char; } } else if (this.bufferLv === 1 && this.buffer) { this.buffer += '}'; try { const bufferJson = JSON.parse(this.buffer); await this.onChunk(bufferJson); } catch (parseError) { console.error('解析Gemini流数据错误:', parseError); } this.buffer = ''; } } } async handleRemainingBuffer() { if (this.buffer.trim() && this.bufferLv >= 1) { try { if (!this.buffer.endsWith('}')) { this.buffer += '}'; } const bufferJson = JSON.parse(this.buffer); await this.onChunk(bufferJson); } catch (parseError) { console.error('解析最后的缓冲区数据错误:', parseError); } } } } /** * 认证中间件 */ class AuthMiddleware { constructor(config) { this.config = config; } middleware() { return (req, res, next) => { // 跳过健康检查和预检请求 if (req.path === '/health' || req.method === 'OPTIONS') { return next(); } const authHeader = req.headers.authorization; if (!this.config.validateAuth(authHeader)) { return res.status(401).json({ error: { message: 'Invalid authentication credentials', type: 'invalid_request_error', code: 'invalid_api_key' } }); } next(); }; } } /** * API代理服务类 */ class ApiProxyService { constructor() { this.config = new Config(); this.requestBuilder = new GeminiRequestBuilder(this.config); this.modelManager = new ModelManager(this.config); this.authMiddleware = new AuthMiddleware(this.config); } /** * 处理聊天API请求(支持图片) */ async handleChatRequest(req, res) { try { const requestId = `chatcmpl-${uuidv4()}`; const params = MessageConverter.extractParams(req.body); // 异步转换消息(支持图片处理) const geminiMessages = await MessageConverter.convertMessages(params.messages); if (!geminiMessages || geminiMessages.length === 0) { return res.status(400).json({ error: { message: '无效的消息格式或消息为空', type: 'invalid_request_error', code: 'invalid_messages' } }); } const requestBody = this.requestBuilder.buildRequestBody(geminiMessages, params); if (params.stream) { const result = await this.handleStreamRequest(requestBody, params, requestId, res); if (!result.success) { res.status(result.status || 500).json({ error: result.error }); } } else { const result = await this.executeNormalRequest(requestBody, params, requestId); if (result.success) { res.json(result.data); } else { res.status(result.status || 500).json({ error: result.error }); } } } catch (error) { console.error('处理聊天请求错误:', error); res.status(500).json({ error: { message: '内部服务器错误: ' + error.message, type: 'internal_server_error', code: 'server_error' } }); } } /** * 处理假流式聊天API请求 */ async handleFakeStreamChatRequest(req, res) { try { const requestId = `chatcmpl-${uuidv4()}`; const params = MessageConverter.extractParams(req.body); // 异步转换消息(支持图片处理) const geminiMessages = await MessageConverter.convertMessages(params.messages); if (!geminiMessages || geminiMessages.length === 0) { return res.status(400).json({ error: { message: '无效的消息格式或消息为空', type: 'invalid_request_error', code: 'invalid_messages' } }); } console.log("请求中") const requestBody = this.requestBuilder.buildRequestBody(geminiMessages, params); if (params.stream) { // 假流式处理:使用非流式请求,然后模拟流式响应 const result = await this.handleFakeStreamRequest(requestBody, params, requestId, res); if (!result.success) { res.status(result.status || 500).json({ error: result.error }); } } else { // 非流式请求和原来一样 const result = await this.executeNormalRequest(requestBody, params, requestId); if (result.success) { res.json(result.data); } else { res.status(result.status || 500).json({ error: result.error }); } } } catch (error) { console.error('处理假流式聊天请求错误:', error); res.status(500).json({ error: { message: '内部服务器错误: ' + error.message, type: 'internal_server_error', code: 'server_error' } }); } } /** * 处理假流式请求 */ async handleFakeStreamRequest(requestBody, params, requestId, res) { try { // 设置流式响应头 res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' }); // 开始发送ping消息保持连接活跃 const pingInterval = setInterval(() => { try { res.write(': ping\n\n'); } catch (error) { clearInterval(pingInterval); } }, 1000); // 每秒发送一次ping // 执行非流式请求 const result = await this.executeNormalRequest(requestBody, params, requestId); // 停止ping clearInterval(pingInterval); if (!result.success) { res.write(`data: ${JSON.stringify({ error: result.error })}\n\n`); res.write('data: [DONE]\n\n'); res.end(); return { success: false }; } // 获取响应文本 const responseText = result.data.choices[0]?.message?.content || ''; if (responseText) { // 将文本拆分为假流式块 const chunks = ResponseConverter.splitTextToFakeStream(responseText, requestId, params.model); // 逐步发送块,模拟流式响应 for (const chunk of chunks) { res.write(chunk); // 添加小延迟以模拟真实的流式响应 await new Promise(resolve => setTimeout(resolve, 50)); } } res.write('data: [DONE]\n\n'); res.end(); return { success: true }; } catch (error) { console.error('处理假流式请求错误:', error); try { res.write(`data: ${JSON.stringify({ error: '内部服务器错误: ' + error.message })}\n\n`); res.write('data: [DONE]\n\n'); res.end(); } catch (writeError) { console.error('写入错误响应失败:', writeError); } return { success: false, error: error.message }; } } /** * 处理流式请求 */ async handleStreamRequest(requestBody, params, requestId, res, retryCount = 0) { const maxRetries = 3; const apiKey = this.config.getApiKey(); if (!apiKey) { return { success: false, error: '目前暂无可用的API Key', status: 503 }; } try { const apiUrl = this.requestBuilder.buildApiUrl(params.model, apiKey, true); const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); if (response.status === 403) { this.config.markKeyAsInvalid(apiKey); if (retryCount < maxRetries) { return await this.handleStreamRequest(requestBody, params, requestId, res, retryCount + 1); } return { success: false, error: 'API Key 无效', status: 403 }; } if (response.status === 429) { this.config.moveToUsed(apiKey); if (retryCount < maxRetries) { return await this.handleStreamRequest(requestBody, params, requestId, res, retryCount + 1); } return { success: false, error: '请求频率过高,请稍后重试', status: 429 }; } if (response.status === 500) { this.config.moveToUsed(apiKey); return { success: false, error: '目前服务器繁忙,请稍后重试', status: 500 }; } if (!response.ok) { const errorText = await response.text(); console.error(`API请求失败: ${response.status}, 错误信息: ${errorText}`); return { success: false, error: `API请求失败: ${response.status}`, status: response.status }; } res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*' }); const parser = new GeminiRealtimeStreamParser(response, async (geminiData) => { const convertedChunk = ResponseConverter.convertStreamChunk(geminiData, requestId, params.model); if (convertedChunk) { res.write(convertedChunk); } }); await parser.start(); res.write('data: [DONE]\n\n'); res.end(); return { success: true }; } catch (error) { console.error('执行流式请求错误:', error); this.config.moveToUsed(apiKey); if (retryCount < maxRetries) { return await this.handleStreamRequest(requestBody, params, requestId, res, retryCount + 1); } return { success: false, error: '网络请求失败: ' + error.message, status: 500 }; } } /** * 处理非流式请求 */ async executeNormalRequest(requestBody, params, requestId, retryCount = 0) { const maxRetries = 3; const apiKey = this.config.getApiKey(); if (!apiKey) { return { success: false, error: '目前暂无可用的API Key', status: 503 }; } try { const apiUrl = this.requestBuilder.buildApiUrl(params.model, apiKey, false); const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestBody) }); if (response.status === 403) { this.config.markKeyAsInvalid(apiKey); if (retryCount < maxRetries) { return await this.executeNormalRequest(requestBody, params, requestId, retryCount + 1); } return { success: false, error: 'API Key 无效', status: 403 }; } if (response.status === 429) { this.config.moveToUsed(apiKey); if (retryCount < maxRetries) { return await this.executeNormalRequest(requestBody, params, requestId, retryCount + 1); } return { success: false, error: '请求频率过高,请稍后重试', status: 429 }; } if (response.status === 500) { this.config.moveToUsed(apiKey); return { success: false, error: '目前服务器繁忙,请稍后重试', status: 500 }; } if (!response.ok) { const errorText = await response.text(); console.error(`API请求失败: ${response.status}, 错误信息: ${errorText}`); return { success: false, error: `API请求失败: ${response.status}`, status: response.status }; } const geminiResponse = await response.json(); const openaiResponse = ResponseConverter.convertNormalResponse(geminiResponse, requestId, params.model); return { success: true, data: openaiResponse }; } catch (error) { console.error('执行非流式请求错误:', error); this.config.moveToUsed(apiKey); if (retryCount < maxRetries) { return await this.executeNormalRequest(requestBody, params, requestId, retryCount + 1); } return { success: false, error: '网络请求失败: ' + error.message, status: 500 }; } } /** * 处理模型列表请求 */ async handleModelsRequest(req, res) { try { const result = await this.modelManager.getModels(); if (result.success) { res.json(result.data); } else { res.status(result.status || 500).json({ error: result.error }); } } catch (error) { console.error('处理模型列表请求错误:', error); res.status(500).json({ error: '内部服务器错误' }); } } } /** * Express 服务器 */ class Server { constructor() { this.app = express(); this.apiProxy = new ApiProxyService(); this.setupMiddleware(); this.setupRoutes(); } setupMiddleware() { // CORS配置 this.app.use(cors({ origin: '*', credentials: true, optionsSuccessStatus: 200 })); // JSON解析 - 增加大小限制以支持图片 this.app.use(express.json({ limit: '50mb' })); this.app.use(express.urlencoded({ limit: '50mb', extended: true })); // 认证中间件 this.app.use(this.apiProxy.authMiddleware.middleware()); // 请求日志中间件 this.app.use((req, res, next) => { const start = Date.now(); res.on('finish', () => { const duration = Date.now() - start; console.log(`${req.method} ${req.path} - ${res.statusCode} [${duration}ms]`); }); next(); }); } setupRoutes() { // 原始聊天接口(支持图片) this.app.post('/v1/chat/completions', (req, res) => { this.apiProxy.handleChatRequest(req, res); }); // 假流式聊天接口 this.app.post('/fakestream/v1/chat/completions', (req, res) => { this.apiProxy.handleFakeStreamChatRequest(req, res); }); // 原始模型列表接口 this.app.get('/v1/models', (req, res) => { this.apiProxy.handleModelsRequest(req, res); }); // 假流式模型列表接口(逻辑相同) this.app.get('/fakestream/v1/models', (req, res) => { this.apiProxy.handleModelsRequest(req, res); }); // 健康检查接口 this.app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), availableKeys: this.apiProxy.config.apiKeys.length, usedKeys: this.apiProxy.config.usedApiKeys.length, invalidKeys: this.apiProxy.config.invalidApiKeys.length, version: '2.0.0', features: ['text', 'vision', 'stream', 'fake_stream', 'load_balancing'] }); }); // 404处理 this.app.use('*', (req, res) => { res.status(404).json({ error: { message: 'Not Found', type: 'invalid_request_error', code: 'not_found' } }); }); // 全局错误处理 this.app.use((err, req, res, next) => { console.error('服务器错误:', err); res.status(500).json({ error: { message: '内部服务器错误', type: 'internal_server_error', code: 'server_error' } }); }); } start(port = 3000) { this.app.listen(port, () => { console.log(`🚀 OpenAI to Gemini Proxy Server (Enhanced) 启动在端口 ${port}`); console.log(`📍 聊天API: http://localhost:${port}/v1/chat/completions`); console.log(`📍 假流式聊天API: http://localhost:${port}/fakestream/v1/chat/completions`); console.log(`📋 模型列表: http://localhost:${port}/v1/models`); console.log(`📋 假流式模型列表: http://localhost:${port}/fakestream/v1/models`); console.log(`🔍 健康检查: http://localhost:${port}/health`); }); } } // 启动服务器 const server = new Server(); const port = process.env.PORT || 3000; server.start(port);