Spaces:
Paused
Paused
| const express = require('express'); | |
| const morgan = require('morgan'); | |
| const { createProxyMiddleware } = require('http-proxy-middleware'); | |
| const axios = require('axios'); | |
| const fs = require('fs'); | |
| const app = express(); | |
| app.use(morgan('dev')); | |
| app.use(express.json()); | |
| // 生成随机字符串的函数 | |
| function getRandomIDPro({ size, dictType, customDict }) { | |
| let random = ''; | |
| if (!customDict) { | |
| switch (dictType) { | |
| case 'alphabet': | |
| customDict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; | |
| break; | |
| case 'max': | |
| customDict = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-'; | |
| break; | |
| default: | |
| customDict = '0123456789'; | |
| } | |
| } | |
| for (; size--; ) random += customDict[(Math.random() * customDict.length) | 0]; | |
| return random; | |
| } | |
| // 模型列表路由 | |
| app.get('/hf/v1/models', (req, res) => { | |
| const models = { | |
| "object": "list", | |
| "data": [ | |
| { | |
| "id": "claude-3.5-sonnet", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-4", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-4o", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3-opus", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-3.5-turbo", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-4-turbo-2024-04-09", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-4o-128k", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gemini-1.5-flash-500k", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3-haiku-200k", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3-5-sonnet-200k", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3-5-sonnet-20241022", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gpt-4o-mini", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "o1-mini", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "o1-preview", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "o1", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3.5-haiku", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gemini-exp-1206", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gemini-2.0-flash-thinking-exp", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "gemini-2.0-flash-exp", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "deepseek-v3", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "deepseek-r1", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| } | |
| ] | |
| }; | |
| res.json(models); | |
| }); | |
| // 聊天完成度代理 | |
| app.post('/hf/v1/chat/completions', async (req, res) => { | |
| try { | |
| // 生成 checksum | |
| const checksum = `ICMlQsvO${getRandomIDPro({ dictType: 'max', size: 64 })}/${getRandomIDPro({ dictType: 'max', size: 64 })}`; | |
| // 从请求头获取认证信息 | |
| const authHeader = req.headers.authorization; | |
| const token = authHeader?.replace('Bearer ', '') || ''; | |
| // 构建请求头 | |
| const headers = { | |
| 'Authorization': authHeader, | |
| 'Content-Type': 'application/json', | |
| 'x-cursor-checksum': checksum, | |
| 'x-cursor-token': token, | |
| 'User-Agent': 'cursor/0.2.0', | |
| 'Accept': 'application/json' | |
| }; | |
| // 构建请求体 | |
| const requestBody = { | |
| ...req.body, | |
| stream: false | |
| }; | |
| // 转发请求到目标服务器 | |
| const response = await axios.post('http://localhost:3010/v1/chat/completions', requestBody, { | |
| headers: headers, | |
| timeout: 30000 | |
| }); | |
| // 返回响应 | |
| res.status(response.status).json(response.data); | |
| } catch (error) { | |
| console.error('Error in chat completions:', error); | |
| if (error.response) { | |
| res.status(error.response.status).json(error.response.data); | |
| } else { | |
| res.status(500).json({ | |
| error: 'Internal server error', | |
| message: error.message | |
| }); | |
| } | |
| } | |
| }); | |
| // 主页路由 | |
| app.get('/', (req, res) => { | |
| const htmlContent = ` | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cursor To OpenAI</title> | |
| <style> | |
| :root { | |
| --primary-color: #2563eb; | |
| --bg-color: #f8fafc; | |
| --card-bg: #ffffff; | |
| } | |
| body { | |
| padding: 20px; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| line-height: 1.6; | |
| background: var(--bg-color); | |
| color: #1a1a1a; | |
| } | |
| .container { | |
| padding: 20px; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 40px; | |
| } | |
| .header h1 { | |
| color: var(--primary-color); | |
| font-size: 2.5em; | |
| margin-bottom: 10px; | |
| } | |
| .info { | |
| background: #fff; | |
| padding: 25px; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| margin-bottom: 30px; | |
| border: 1px solid #e5e7eb; | |
| } | |
| .info-item { | |
| margin: 15px 0; | |
| padding: 10px; | |
| background: #f8fafc; | |
| border-radius: 8px; | |
| border: 1px solid #e5e7eb; | |
| } | |
| .info-label { | |
| color: #4b5563; | |
| font-size: 0.9em; | |
| margin-bottom: 5px; | |
| } | |
| .info-value { | |
| color: var(--primary-color); | |
| font-weight: 500; | |
| } | |
| .models { | |
| background: var(--card-bg); | |
| padding: 25px; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| border: 1px solid #e5e7eb; | |
| } | |
| .models h3 { | |
| color: #1a1a1a; | |
| margin-bottom: 20px; | |
| font-size: 1.5em; | |
| border-bottom: 2px solid #e5e7eb; | |
| padding-bottom: 10px; | |
| } | |
| .model-item { | |
| margin: 12px 0; | |
| padding: 15px; | |
| background: #f8fafc; | |
| border-radius: 8px; | |
| border: 1px solid #e5e7eb; | |
| transition: all 0.3s ease; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .model-item:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | |
| } | |
| .model-name { | |
| font-weight: 500; | |
| color: #1a1a1a; | |
| } | |
| .model-provider { | |
| color: #6b7280; | |
| font-size: 0.9em; | |
| padding: 4px 8px; | |
| background: #f1f5f9; | |
| border-radius: 4px; | |
| } | |
| @media (max-width: 768px) { | |
| body { | |
| padding: 10px; | |
| } | |
| .container { | |
| padding: 10px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>Cursor To OpenAI Server</h1> | |
| <p>高性能 AI 模型代理服务</p> | |
| </div> | |
| <div class="info"> | |
| <h2>配置信息</h2> | |
| <div class="info-item"> | |
| <div class="info-label">聊天来源</div> | |
| <div class="info-value">自定义(兼容 OpenAI)</div> | |
| </div> | |
| <div class="info-item"> | |
| <div class="info-label">自定义端点(基本URL)</div> | |
| <div class="info-value" id="endpoint-url"></div> | |
| </div> | |
| <div class="info-item"> | |
| <div class="info-label">自定义API密钥</div> | |
| <div class="info-value">抓取的Cursor Cookie,格式为user_...</div> | |
| </div> | |
| </div> | |
| <div class="models"> | |
| <h3>支持的模型列表</h3> | |
| <div id="model-list"></div> | |
| </div> | |
| </div> | |
| <script> | |
| const url = new URL(window.location.href); | |
| const link = url.protocol + '//' + url.host + '/hf/v1'; | |
| document.getElementById('endpoint-url').textContent = link; | |
| fetch(link + '/models') | |
| .then(response => response.json()) | |
| .then(data => { | |
| const modelList = document.getElementById('model-list'); | |
| data.data.forEach(model => { | |
| const div = document.createElement('div'); | |
| div.className = 'model-item'; | |
| div.innerHTML = | |
| '<span class="model-name">' + model.id + '</span>' + | |
| '<span class="model-provider">' + model.owned_by + '</span>'; | |
| modelList.appendChild(div); | |
| }); | |
| }) | |
| .catch(error => { | |
| console.error('Error fetching models:', error); | |
| document.getElementById('model-list').textContent = '获取模型列表失败'; | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| `; | |
| res.send(htmlContent); | |
| }); | |
| const port = process.env.HF_PORT || 7860; | |
| app.listen(port, () => { | |
| console.log(`HF Proxy server is running at PORT: ${port}`); | |
| }); | |