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`);
});