api / server.js
Juanoto2012's picture
Update server.js
ccb18a3 verified
Raw
History Blame Contribute Delete
5.3 kB
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`);
});