| const express = require('express'); |
| const axios = require('axios'); |
|
|
| const app = express(); |
| const PORT = process.env.PORT || 7860; |
|
|
| const TARGET_API = process.env.TARGET_API || "https://huggingface.co"; |
|
|
| let PATH_MAPPINGS = { "/": "/" }; |
| try { |
| console.log(`Target API: ${TARGET_API}`); |
| if (process.env.PATH_MAPPINGS) { |
| PATH_MAPPINGS = JSON.parse(process.env.PATH_MAPPINGS); |
| } |
| } catch (e) { |
| console.error("解析 PATH_MAPPINGS 失败,使用默认配置:", e.message); |
| PATH_MAPPINGS = { "/": "/" }; |
| } |
|
|
| app.get('/', (req, res) => { |
| res.send("service running."); |
| }); |
|
|
| |
| const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); |
|
|
| app.use(async (req, res) => { |
| let fullPath = req.path; |
|
|
| |
| for (const [originalPath, newPath] of Object.entries(PATH_MAPPINGS)) { |
| if (fullPath.startsWith(originalPath)) { |
| fullPath = fullPath.replace(originalPath, newPath); |
| break; |
| } |
| } |
|
|
| const targetUrl = `${TARGET_API}${fullPath}`; |
|
|
| |
| const headers = { ...req.headers }; |
| delete headers.host; |
|
|
| |
| |
| let bodyBuffer = null; |
| const method = req.method.toUpperCase(); |
| if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) { |
| const chunks = []; |
| for await (const chunk of req) { |
| chunks.push(chunk); |
| } |
| bodyBuffer = Buffer.concat(chunks); |
| } |
|
|
| |
| let aborted = false; |
| const onAbort = () => { aborted = true; }; |
| res.on('close', onAbort); |
|
|
| const MAX_RETRY = 20; |
| let attempt = 0; |
|
|
| try { |
| while (attempt < MAX_RETRY) { |
| attempt++; |
| if (aborted) return; |
|
|
| |
| const response = await axios({ |
| method: req.method, |
| url: targetUrl, |
| headers: headers, |
| params: req.query, |
| data: bodyBuffer, |
| responseType: 'stream', |
| validateStatus: () => true |
| }); |
|
|
| |
| if (response.status === 429 && attempt < MAX_RETRY) { |
| |
| try { |
| response.data.on('error', () => {}); |
| response.data.destroy(); |
| } catch (e) {} |
|
|
| const delay = 2000 + Math.random() * 3000; |
| console.log(`收到 429,第 ${attempt} 次请求,将在 ${Math.round(delay)}ms 后重试(剩余 ${MAX_RETRY - attempt} 次)`); |
|
|
| await sleep(delay); |
| if (aborted) return; |
| continue; |
| } |
|
|
| |
| res.status(response.status); |
|
|
| const skipHeaders = ['content-length', 'transfer-encoding', 'connection']; |
| Object.entries(response.headers).forEach(([key, value]) => { |
| if (!skipHeaders.includes(key.toLowerCase())) { |
| res.setHeader(key, value); |
| } |
| }); |
|
|
| response.data.pipe(res); |
| return; |
| } |
| } catch (error) { |
| console.error(`代理请求失败: ${error.message}`); |
| if (!res.headersSent) { |
| res.status(500).send("Proxy Error"); |
| } |
| } finally { |
| res.off('close', onAbort); |
| } |
| }); |
|
|
| app.listen(PORT, '0.0.0.0', () => { |
| console.log(`服务已在端口 ${PORT} 启动`); |
| }); |