Spaces:
Paused
Paused
| const express = require('express'); | |
| const morgan = require('morgan'); | |
| const { createProxyMiddleware } = require('http-proxy-middleware'); | |
| const url = require('url'); | |
| const app = express(); | |
| app.use(morgan('dev')); | |
| // 从环境变量获取代理配置 | |
| const proxyUrl = process.env.PROXY || ''; | |
| console.log(`Proxy configuration: ${proxyUrl ? '已配置' : '未配置'}`); | |
| // 解析代理URL | |
| let proxyConfig = null; | |
| if (proxyUrl) { | |
| try { | |
| const parsedUrl = url.parse(proxyUrl); | |
| proxyConfig = { | |
| host: parsedUrl.hostname, | |
| port: parsedUrl.port || 80, | |
| auth: parsedUrl.auth ? { | |
| username: parsedUrl.auth.split(':')[0], | |
| password: parsedUrl.auth.split(':')[1] | |
| } : undefined | |
| }; | |
| // 打印代理配置(安全处理密码) | |
| const maskedConfig = { | |
| ...proxyConfig, | |
| auth: proxyConfig.auth ? { | |
| username: proxyConfig.auth.username, | |
| password: '******' | |
| } : undefined | |
| }; | |
| console.log('Using proxy:', JSON.stringify(maskedConfig)); | |
| } catch (error) { | |
| console.error('Failed to parse proxy URL:', error.message); | |
| } | |
| } | |
| // 添加模型列表API | |
| 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" | |
| }, | |
| // 新增模型 | |
| { | |
| "id": "claude-3.7-sonnet", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| }, | |
| { | |
| "id": "claude-3.7-sonnet-thinking", | |
| "object": "model", | |
| "created": 1706745938, | |
| "owned_by": "cursor" | |
| } | |
| ] | |
| }; | |
| res.json(models); | |
| }); | |
| // 配置代理中间件 | |
| app.use('/hf/v1/chat/completions', createProxyMiddleware({ | |
| target: 'http://localhost:3010/v1/chat/completions', | |
| changeOrigin: true, | |
| // 添加代理配置 | |
| proxy: proxyConfig, | |
| // 增加错误处理 | |
| onError: (err, req, res) => { | |
| console.error('Proxy error:', err); | |
| res.status(500).send('Proxy error occurred: ' + err.message); | |
| }, | |
| onProxyReq: (proxyReq, req, res) => { | |
| console.log(`Proxying request to chat completions ${proxyConfig ? 'using proxy' : 'directly'}`); | |
| }, | |
| onProxyRes: (proxyRes, req, res) => { | |
| console.log(`Received response with status: ${proxyRes.statusCode}`); | |
| } | |
| })); | |
| 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> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| line-height: 1.6; | |
| } | |
| .container { | |
| background: #f9f9f9; | |
| border-radius: 10px; | |
| padding: 20px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| } | |
| .info-item { | |
| margin-bottom: 10px; | |
| } | |
| .status { | |
| background: ${proxyConfig ? '#e1f5e1' : '#fff3cd'}; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin-top: 20px; | |
| border: 1px solid ${proxyConfig ? '#c3e6cb' : '#ffeeba'}; | |
| } | |
| .models-container { | |
| margin-top: 20px; | |
| border-top: 1px solid #eee; | |
| padding-top: 20px; | |
| } | |
| .model-list { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); | |
| gap: 10px; | |
| margin-top: 15px; | |
| } | |
| .model-item { | |
| background: #f0f0f0; | |
| padding: 8px 12px; | |
| border-radius: 4px; | |
| font-size: 0.9em; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Cursor To OpenAI Server</h1> | |
| <div class="info-item"> | |
| <strong>聊天来源:</strong> 自定义(兼容 OpenAI) | |
| </div> | |
| <div class="info-item"> | |
| <strong>自定义端点(基本URL):</strong><span id="endpoint-url"></span> | |
| </div> | |
| <div class="info-item"> | |
| <strong>自定义API密钥:</strong>[抓取的Cursor Cookie,格式为user_...] | |
| </div> | |
| <div class="status"> | |
| <strong>代理状态:</strong> ${proxyConfig ? '已启用' : '未启用'} | |
| </div> | |
| <div class="models-container"> | |
| <h3>支持的模型</h3> | |
| <div id="model-list" class="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('/hf/v1/models') | |
| .then(response => response.json()) | |
| .then(data => { | |
| const modelListEl = document.getElementById('model-list'); | |
| modelListEl.innerHTML = ''; | |
| data.data.forEach(model => { | |
| const modelEl = document.createElement('div'); | |
| modelEl.className = 'model-item'; | |
| modelEl.textContent = model.id; | |
| modelListEl.appendChild(modelEl); | |
| }); | |
| }) | |
| .catch(err => { | |
| document.getElementById('model-list').textContent = '加载模型失败: ' + err.message; | |
| }); | |
| </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}`); | |
| console.log(`Proxy status: ${proxyConfig ? 'Enabled' : 'Disabled'}`); | |
| }); | |