import { Router } from 'express'; import { randomUUID } from 'crypto'; import { createLogger } from '../utils/logger.js'; import { config } from '../config/index.js'; import { notionClient } from '../services/NotionClient.js'; import { streamManager } from '../services/StreamManager.js'; import { cookieManager } from '../CookieManager.js'; import { authenticate, addSessionToken } from '../middleware/auth.js'; import crypto from 'crypto'; const logger = createLogger('APIRouter'); const router = Router(); /** * POST /admin/login * 管理员登录端点 */ router.post('/admin/login', async (req, res) => { try { const { username, password } = req.body; if (!username || !password) { return res.status(400).json({ success: false, message: '请提供用户名和密码' }); } // 从环境变量获取管理员凭据 const adminUsername = process.env.ADMIN_USERNAME || 'admin'; const adminPassword = process.env.ADMIN_PASSWORD || process.env.AUTH_TOKEN || 'admin123'; // 验证用户名和密码 if (username !== adminUsername || password !== adminPassword) { return res.status(401).json({ success: false, message: '用户名或密码错误' }); } // 生成会话令牌 const sessionToken = crypto.randomBytes(32).toString('hex'); // 保存会话令牌 const user = { username: adminUsername }; addSessionToken(sessionToken, user); // 返回登录成功信息 res.json({ success: true, user: user, token: sessionToken, message: '登录成功' }); logger.info(`管理员 ${username} 登录成功`); } catch (error) { logger.error(`登录失败: ${error.message}`, error); res.status(500).json({ success: false, message: '登录失败,请稍后重试' }); } }); /** * GET /v1/models * 返回可用的模型列表 */ router.get('/v1/models', authenticate, (req, res) => { const modelList = { data: config.availableModels.map(id => ({ id })) }; res.json(modelList); }); /** * POST /v1/chat/completions * 处理聊天完成请求 */ router.post('/v1/chat/completions', authenticate, async (req, res) => { const clientId = req.headers['x-client-id'] || randomUUID(); try { // 验证系统状态 const status = notionClient.getStatus(); if (!status.initialized) { return res.status(500).json({ error: { message: "系统未成功初始化。请检查您的NOTION_COOKIE是否有效。", type: "server_error" } }); } if (status.validCookies === 0) { return res.status(500).json({ error: { message: "没有可用的有效cookie。请检查您的NOTION_COOKIE配置。", type: "server_error" } }); } // 验证请求数据 const requestData = req.body; const validation = validateChatRequest(requestData); if (!validation.valid) { return res.status(400).json({ error: { message: validation.error, type: "invalid_request_error" } }); } // 构建Notion请求 const notionRequestBody = notionClient.buildRequest(requestData); // 处理流式响应 if (requestData.stream) { await handleStreamResponse(req, res, clientId, notionRequestBody); } else { await handleNonStreamResponse(req, res, clientId, notionRequestBody, requestData); } } catch (error) { logger.error(`聊天完成端点错误: ${error.message}`, error); if (!res.headersSent) { res.status(500).json({ error: { message: `Internal server error: ${error.message}`, type: "server_error" } }); } } }); /** * GET /health * 健康检查端点 */ router.get('/health', (req, res) => { const status = notionClient.getStatus(); res.json({ status: 'ok', timestamp: new Date().toISOString(), initialized: status.initialized, valid_cookies: status.validCookies, active_streams: streamManager.getActiveCount() }); }); /** * GET /cookies/status * Cookie状态查询端点 */ router.get('/cookies/status', authenticate, (req, res) => { res.json({ total_cookies: cookieManager.getValidCount(), cookies: cookieManager.getStatus() }); }); /** * POST /cookies/add * 添加新Cookie */ router.post('/cookies/add', authenticate, async (req, res) => { try { const { cookies, threadId } = req.body; if (!cookies) { return res.status(400).json({ error: { message: '请提供cookie内容' } }); } // 支持批量添加 const cookieArray = cookies.includes('|') ? cookies.split('|') : [cookies]; let added = 0; let failed = 0; const errors = []; for (const cookie of cookieArray) { const trimmedCookie = cookie.trim(); if (!trimmedCookie) continue; const result = await cookieManager.addCookie(trimmedCookie, threadId); if (result.success) { added++; } else { failed++; errors.push(result.error); } } // 如果有成功添加的cookie,保存到cookies.txt文件 if (added > 0 && config.cookie.filePath) { try { cookieManager.saveToFile(config.cookie.filePath, true); logger.info(`已将更新后的cookie保存到文件: ${config.cookie.filePath}`); } catch (saveError) { logger.error(`保存cookie到文件失败: ${saveError.message}`); } } res.json({ success: true, added, failed, errors: errors.length > 0 ? errors : undefined }); } catch (error) { logger.error(`添加Cookie失败: ${error.message}`, error); res.status(500).json({ error: { message: `添加Cookie失败: ${error.message}` } }); } }); /** * PUT /cookies/thread * 更新Cookie的Thread ID */ router.put('/cookies/thread', authenticate, (req, res) => { try { const { userId, threadId } = req.body; if (!userId) { return res.status(400).json({ error: { message: '请提供用户ID' } }); } const success = cookieManager.setThreadId(userId, threadId); if (success) { res.json({ success: true }); } else { res.status(404).json({ error: { message: '未找到指定用户的Cookie' } }); } } catch (error) { logger.error(`更新Thread ID失败: ${error.message}`, error); res.status(500).json({ error: { message: `更新Thread ID失败: ${error.message}` } }); } }); /** * DELETE /cookies/:userId * 删除指定用户的Cookie */ router.delete('/cookies/:userId', authenticate, (req, res) => { try { const { userId } = req.params; const success = cookieManager.deleteCookie(userId); if (success) { // 删除成功后,保存到cookies.txt文件 if (config.cookie.filePath) { try { cookieManager.saveToFile(config.cookie.filePath, true); logger.info(`已将更新后的cookie保存到文件: ${config.cookie.filePath}`); } catch (saveError) { logger.error(`保存cookie到文件失败: ${saveError.message}`); } } res.json({ success: true }); } else { res.status(404).json({ error: { message: '未找到指定用户的Cookie' } }); } } catch (error) { logger.error(`删除Cookie失败: ${error.message}`, error); res.status(500).json({ error: { message: `删除Cookie失败: ${error.message}` } }); } }); /** * POST /cookies/refresh * 刷新所有Cookie状态 */ router.post('/cookies/refresh', authenticate, async (req, res) => { try { // 重新验证所有cookie const cookies = cookieManager.getStatus(); let refreshed = 0; for (const cookie of cookies) { // 这里可以添加重新验证逻辑 // 暂时只返回成功 refreshed++; } res.json({ success: true, refreshed, total: cookies.length }); } catch (error) { logger.error(`刷新Cookie状态失败: ${error.message}`, error); res.status(500).json({ error: { message: `刷新失败: ${error.message}` } }); } }); /** * PUT /cookies/:userId/toggle * 切换Cookie的启用状态 */ router.put('/cookies/:userId/toggle', authenticate, (req, res) => { try { const { userId } = req.params; const { enabled } = req.body; if (typeof enabled !== 'boolean') { return res.status(400).json({ error: { message: '请提供有效的enabled状态(true/false)' } }); } const success = cookieManager.toggleCookie(userId, enabled); if (success) { res.json({ success: true, enabled }); } else { res.status(404).json({ error: { message: '未找到指定用户的Cookie' } }); } } catch (error) { logger.error(`切换Cookie状态失败: ${error.message}`, error); res.status(500).json({ error: { message: `切换状态失败: ${error.message}` } }); } }); /** * 验证聊天请求数据 */ function validateChatRequest(requestData) { if (!requestData.messages) { return { valid: false, error: "Invalid request: 'messages' field is required." }; } if (!Array.isArray(requestData.messages)) { return { valid: false, error: "Invalid request: 'messages' field must be an array." }; } if (requestData.messages.length === 0) { return { valid: false, error: "Invalid request: 'messages' field must be a non-empty array." }; } // 验证每个消息的格式 for (const message of requestData.messages) { if (!message.role || !['system', 'user', 'assistant'].includes(message.role)) { return { valid: false, error: "Invalid message format: each message must have a valid 'role' field." }; } if (message.content === undefined || message.content === null) { return { valid: false, error: "Invalid message format: each message must have a 'content' field." }; } } return { valid: true }; } /** * 处理流式响应 */ async function handleStreamResponse(req, res, clientId, notionRequestBody) { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); logger.info(`开始流式响应 - 客户端: ${clientId}`); const stream = await notionClient.createStream(notionRequestBody); // 注册流 streamManager.register(clientId, stream); // 将流连接到响应 stream.pipe(res); // 处理客户端断开连接 req.on('close', () => { logger.info(`客户端 ${clientId} 断开连接`); streamManager.close(clientId); }); // 处理流错误 stream.on('error', (error) => { logger.error(`流错误 - 客户端 ${clientId}: ${error.message}`); if (!res.headersSent) { res.status(500).json({ error: { message: `Stream error: ${error.message}`, type: "server_error" } }); } }); } /** * 处理非流式响应 */ async function handleNonStreamResponse(req, res, clientId, notionRequestBody, requestData) { logger.info(`开始非流式响应 - 客户端: ${clientId}`); const chunks = []; const stream = await notionClient.createStream(notionRequestBody); // 注册流 streamManager.register(clientId, stream); return new Promise((resolve, reject) => { stream.on('data', (chunk) => { const chunkStr = chunk.toString(); if (chunkStr.startsWith('data: ') && !chunkStr.includes('[DONE]')) { try { const dataJson = chunkStr.substring(6).trim(); if (dataJson) { const chunkData = JSON.parse(dataJson); if (chunkData.choices && chunkData.choices[0].delta && chunkData.choices[0].delta.content) { chunks.push(chunkData.choices[0].delta.content); } } } catch (error) { logger.error(`解析非流式响应块时出错: ${error.message}`); } } }); stream.on('end', () => { const fullResponse = { id: `chatcmpl-${randomUUID()}`, object: "chat.completion", created: Math.floor(Date.now() / 1000), model: requestData.model, choices: [ { index: 0, message: { role: "assistant", content: chunks.join('') }, finish_reason: "stop" } ], usage: { prompt_tokens: null, completion_tokens: null, total_tokens: null } }; res.json(fullResponse); resolve(); }); stream.on('error', (error) => { logger.error(`非流式响应出错: ${error.message}`); reject(error); }); // 处理客户端断开连接 req.on('close', () => { logger.info(`客户端 ${clientId} 断开连接(非流式)`); streamManager.close(clientId); }); }); } export default router;