File size: 5,298 Bytes
41ef955 ccb18a3 41ef955 ccb18a3 41ef955 b7b6c51 41ef955 b351d1f d9c6f10 b351d1f 82ed7bf d9c6f10 fbdbcb7 41ef955 ccb18a3 41ef955 ccb18a3 d9c6f10 ccb18a3 b351d1f 41ef955 b351d1f ccb18a3 41ef955 ccb18a3 82ed7bf 81a789f ccb18a3 81a789f 870c749 ccb18a3 82ed7bf ccb18a3 81a789f ccb18a3 82ed7bf ccb18a3 82ed7bf ccb18a3 82ed7bf ccb18a3 81a789f ccb18a3 81a789f 870c749 ccb18a3 870c749 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | import express from 'express';
import cors from 'cors';
import rateLimit from 'express-rate-limit';
import { Readable } from 'stream';
const app = express();
const PORT = 7860;
// --- CONFIGURACIÓN DE SEGURIDAD PRIVADA ---
app.set('trust proxy', 1);
app.disable('x-powered-by');
app.use(cors());
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
res.setHeader('Referrer-Policy', 'no-referrer');
next();
});
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
// --- RATE LIMITING ALTO (500 CONCURRENTES) ---
// Permitimos 500 peticiones por minuto por IP local.
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 500,
keyGenerator: (req) => req.ip,
message: { error: { message: "Servidor a máxima capacidad. Intenta en unos segundos.", code: 429 } },
standardHeaders: false,
legacyHeaders: false,
});
app.get('/health', (req, res) => {
res.json({ status: "online", type: "High Concurrency Proxy", max_capacity: 500 });
});
// --- MODELOS DISPONIBLES ---
app.get('/v1/models', (req, res) => {
res.json({
object: "list",
data: [
{ id: "gpt-4o", object: "model", type: "text", owned_by: "system" },
{ id: "claude-3-5-sonnet", object: "model", type: "text", owned_by: "system" },
{ id: "gemini-1.5-pro", object: "model", type: "text", owned_by: "system" }
]
});
});
// --- POOL DE PROVEEDORES ---
// Usando api.llm7.io como solicitaste. Puedes añadir más separados por comas.
const PROVIDERS = [
"https://api.llm7.io/v1/chat/completions"
];
let providerIndex = 0;
// Distribuye las peticiones si hay más de un proveedor en el array
function getNextProvider() {
const url = PROVIDERS[providerIndex];
providerIndex = (providerIndex + 1) % PROVIDERS.length;
return url;
}
// --- EL PROXY DE ALTO RENDIMIENTO ---
app.post(['/v1/chat/completions'], limiter, async (req, res) => {
const clientIp = req.ip || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
// Configuración de Timeout para evitar que 500 conexiones se queden colgadas en memoria
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 45000); // 45 segundos de tiempo de vida
// Reintento interno: si un proveedor falla, intenta con el siguiente antes de darle error al usuario
let attempts = 0;
let responseSent = false;
let lastError = null;
while (attempts < PROVIDERS.length && !responseSent) {
const targetUrl = getNextProvider();
attempts++;
try {
const fetchHeaders = {
"Content-Type": "application/json",
"Accept": "*/*",
"X-Forwarded-For": clientIp, // Delegar IP divide el rate limit en el proveedor final
"Authorization": "Bearer sk-dummy-key-12345" // Clave falsa para proveedores que validan sintaxis
};
const response = await fetch(targetUrl, {
method: "POST",
headers: fetchHeaders,
body: JSON.stringify(req.body),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
lastError = `HTTP ${response.status} de ${targetUrl}`;
continue; // Falla este proveedor, el while loop intentará con el siguiente
}
const responseHeaders = new Headers(response.headers);
res.writeHead(response.status, {
'Content-Type': responseHeaders.get('content-type') || 'application/json',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive' // Vital para alta concurrencia
});
if (response.body) {
const stream = Readable.fromWeb(response.body);
stream.pipe(res);
req.on('close', () => {
stream.destroy(); // Limpiar memoria si el usuario cierra la conexión de golpe
});
} else {
res.end();
}
responseSent = true;
} catch (err) {
lastError = err.name === 'AbortError' ? 'Timeout excedido' : err.message;
// Sigue intentando con el siguiente proveedor
}
}
if (!responseSent) {
clearTimeout(timeoutId);
if (!res.headersSent) {
res.status(503).json({
error: {
message: "Todos los proveedores base rechazaron la conexión simultánea. Intenta reducir la concurrencia.",
details: lastError,
code: 503
}
});
}
}
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`[SYS] Ventarys API v2 corriendo en puerto: ${PORT}`);
console.log(`[SYS] Capacidad: 500 req/min. Conectado a: api.llm7.io`);
}); |