isididiidid's picture
Update hf.js
d3db86c verified
raw
history blame
10.4 kB
const express = require('express');
const morgan = require('morgan');
const { createProxyMiddleware } = require('http-proxy-middleware');
const axios = require('axios');
const fs = require('fs');
const url = require('url'); // 用于解析代理 URL
const app = express();
app.use(morgan('dev'));
// 从环境变量获取代理池
const proxyPool = process.env.PROXY ? process.env.PROXY.split(',').map(p => p.trim()) : [];
if (proxyPool.length > 0) {
console.log('Proxy pool initialized with:', proxyPool);
} else {
console.log('No proxies configured in PROXY environment variable');
}
// 从代理池中随机选择一个代理
function getRandomProxy() {
if (proxyPool.length === 0) return null;
const randomIndex = Math.floor(Math.random() * proxyPool.length);
const proxyUrl = proxyPool[randomIndex];
const parsedUrl = url.parse(proxyUrl);
return {
host: parsedUrl.hostname,
port: parsedUrl.port || 80,
auth: parsedUrl.auth ? {
username: parsedUrl.auth.split(':')[0],
password: parsedUrl.auth.split(':')[1]
} : undefined
};
}
// 配置 axios 的代理
function configureAxiosProxy() {
const proxy = getRandomProxy();
if (proxy) {
axios.defaults.proxy = proxy;
console.log(`Axios using proxy: ${proxy.host}:${proxy.port}`);
} else {
delete axios.defaults.proxy; // 如果没有代理,移除默认代理
}
}
// 模型列表 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"
}
]
};
res.json(models);
});
// 代理转发,使用代理池
app.use('/hf/v1/chat/completions', (req, res, next) => {
const proxy = getRandomProxy();
const middleware = createProxyMiddleware({
target: 'http://localhost:3010/v1/chat/completions',
changeOrigin: true,
proxy: proxy ? proxy : undefined, // 如果有代理则使用
onProxyReq: (proxyReq, req, res) => {
if (req.body) {
const bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
proxyReq.end();
}
},
onError: (err, req, res) => {
console.error('Proxy error:', err);
res.status(500).send('Proxy error occurred');
}
});
if (proxy) {
console.log(`Request proxied via ${proxy.host}:${proxy.port}`);
}
middleware(req, res, next);
});
// 首页
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);
});
// 启动服务前配置 axios 代理
configureAxiosProxy();
const port = process.env.HF_PORT || 7860;
app.listen(port, () => {
console.log(`HF Proxy server is running at PORT: ${port}`);
});