import { handleModelListRequest, handleContentGenerationRequest, API_ACTIONS, ENDPOINT_TYPE } from '../utils/common.js'; import { getProviderPoolManager } from './service-manager.js'; /** * Handle API authentication and routing * @param {string} method - The HTTP method * @param {string} path - The request path * @param {http.IncomingMessage} req - The HTTP request object * @param {http.ServerResponse} res - The HTTP response object * @param {Object} currentConfig - The current configuration object * @param {Object} apiService - The API service instance * @param {Object} providerPoolManager - The provider pool manager instance * @param {string} promptLogFilename - The prompt log filename * @returns {Promise} - True if the request was handled by API */ export async function handleAPIRequests(method, path, req, res, currentConfig, apiService, providerPoolManager, promptLogFilename) { // Route model list requests if (method === 'GET') { if (path === '/v1/models') { await handleModelListRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_MODEL_LIST, currentConfig, providerPoolManager, currentConfig.uuid); return true; } if (path === '/v1beta/models') { await handleModelListRequest(req, res, apiService, ENDPOINT_TYPE.GEMINI_MODEL_LIST, currentConfig, providerPoolManager, currentConfig.uuid); return true; } } // Route content generation requests if (method === 'POST') { if (path === '/v1/chat/completions') { await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_CHAT, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); return true; } if (path === '/v1/responses') { await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.OPENAI_RESPONSES, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); return true; } const geminiUrlPattern = new RegExp(`/v1beta/models/(.+?):(${API_ACTIONS.GENERATE_CONTENT}|${API_ACTIONS.STREAM_GENERATE_CONTENT})`); if (geminiUrlPattern.test(path)) { await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.GEMINI_CONTENT, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); return true; } if (path === '/v1/messages') { await handleContentGenerationRequest(req, res, apiService, ENDPOINT_TYPE.CLAUDE_MESSAGE, currentConfig, promptLogFilename, providerPoolManager, currentConfig.uuid, path); return true; } } return false; } /** * Initialize API management features * @param {Object} services - The initialized services * @returns {Function} - The heartbeat and token refresh function */ export function initializeAPIManagement(services) { const providerPoolManager = getProviderPoolManager(); return async function heartbeatAndRefreshToken() { console.log(`[Heartbeat] Server is running. Current time: ${new Date().toLocaleString()}`, Object.keys(services)); // 循环遍历所有已初始化的服务适配器,并尝试刷新令牌 // if (getProviderPoolManager()) { // await getProviderPoolManager().performHealthChecks(); // 定期执行健康检查 // } for (const providerKey in services) { const serviceAdapter = services[providerKey]; try { // For pooled providers, refreshToken should be handled by individual instances // For single instances, this remains relevant if (serviceAdapter.config?.uuid && providerPoolManager) { providerPoolManager._enqueueRefresh(serviceAdapter.config.MODEL_PROVIDER, { config: serviceAdapter.config, uuid: serviceAdapter.config.uuid }); } else { await serviceAdapter.refreshToken(); } // console.log(`[Token Refresh] Refreshed token for ${providerKey}`); } catch (error) { console.error(`[Token Refresh Error] Failed to refresh token for ${providerKey}: ${error.message}`); // 如果是号池中的某个实例刷新失败,这里需要捕获并更新其状态 // 现有的 serviceInstances 存储的是每个配置对应的单例,而非池中的成员 // 这意味着如果一个池成员的 token 刷新失败,需要找到它并更新其在 poolManager 中的状态 // 暂时通过捕获错误日志来发现问题,更精细的控制需要在 refreshToken 中抛出更多信息 } } }; } /** * Helper function to read request body * @param {http.IncomingMessage} req The HTTP request object. * @returns {Promise} The request body as string. */ export function readRequestBody(req) { return new Promise((resolve, reject) => { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { resolve(body); }); req.on('error', err => { reject(err); }); }); }