Juanoto2012 commited on
Commit
0bb17cf
·
verified ·
1 Parent(s): 475b727

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +21 -26
server.js CHANGED
@@ -8,17 +8,17 @@ const app = express();
8
  const PORT = 7860;
9
 
10
  // --- 1. CONFIGURACIÓN DE SEGURIDAD PARANOICA ---
 
11
  app.set('trust proxy', 1);
12
  app.disable('x-powered-by');
13
  app.use(cors());
14
 
15
  app.use((req, res, next) => {
16
- // Cabeceras estrictas anti-rastreo y anti-inyección
17
  res.setHeader('X-Content-Type-Options', 'nosniff');
18
  res.setHeader('X-Frame-Options', 'DENY');
19
  res.setHeader('X-XSS-Protection', '1; mode=block');
20
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
21
- res.setHeader('Referrer-Policy', 'no-referrer');
22
  res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none';");
23
  next();
24
  });
@@ -27,12 +27,10 @@ app.use(express.json({ limit: '50mb' }));
27
  app.use(express.urlencoded({ limit: '50mb', extended: true }));
28
 
29
  function logError(providerId, reason) {
30
- // CRÍTICO: No registrar NUNCA fechas exactas al milisegundo, ni IPs, ni el tamaño del prompt.
31
- // Solo registramos caídas de la infraestructura.
32
  console.error(`[SYSTEM] Provider Error: ${providerId} | ${reason}`);
33
  }
34
 
35
- // Generador de User-Agents falsos para ofuscar la huella digital del usuario
36
  function getRandomUserAgent() {
37
  const agents = [
38
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
@@ -60,19 +58,19 @@ const PROVIDERS = [
60
  }
61
  ];
62
 
63
- const MAX_PER_PROVIDER = 100;
64
- const QUEUE_TIMEOUT = 25000;
 
 
65
  let currentLoad = { "pollinations-ai": 0, "kepler-cloud": 0, "voids-api": 0 };
66
 
67
  // --- RATE LIMITING LOCAL ---
68
- // Mantenemos el rate limit por IP para que no te tumben EL SERVIDOR,
69
- // pero esta IP jamás saldrá de aquí hacia los proveedores.
70
  const limiter = rateLimit({
71
  windowMs: 60 * 1000,
72
- max: 50,
73
  keyGenerator: (req) => req.ip,
74
  message: { error: { message: "Too many requests.", code: 429 } },
75
- standardHeaders: false, // Desactivar para no dar pistas del rate limit
76
  legacyHeaders: false,
77
  });
78
 
@@ -99,7 +97,7 @@ async function fetchAllModels() {
99
  method: "GET",
100
  headers: {
101
  "Content-Type": "application/json",
102
- "User-Agent": getRandomUserAgent() // Ocultar que somos un proxy Node.js
103
  }
104
  });
105
  if (!resp.ok) return [];
@@ -136,7 +134,6 @@ async function fetchAllModels() {
136
  }
137
 
138
  app.get('/health', (req, res) => {
139
- // Info mínima para no dar detalles de la infraestructura a atacantes
140
  res.json({ status: "online" });
141
  });
142
 
@@ -182,10 +179,11 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
182
  const startTime = Date.now();
183
  let responseSent = false;
184
 
185
- // --- AMNESIA: Extracción profunda del body ---
186
- // Clonamos el contenido para enviarlo y borraremos las referencias inmediatamente.
 
187
  let payload = JSON.stringify(req.body);
188
- req.body = null; // Eliminamos el acceso al body de la petición original
189
 
190
  while (availableProviders.length > 0 && Date.now() - startTime < QUEUE_TIMEOUT) {
191
  let selectedProvider = null;
@@ -215,13 +213,14 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
215
  try {
216
  let targetUrl = isImage && selectedProvider.imageUrl ? selectedProvider.imageUrl : selectedProvider.url;
217
 
218
- // --- OFUSCACIÓN ABSOLUTA ---
219
- // Jamás enviamos IPs, Hostnames originales ni Referers.
220
  const fetchHeaders = {
221
  "Content-Type": "application/json",
222
  "User-Agent": getRandomUserAgent(),
223
  "Accept": "*/*",
224
- "Connection": "keep-alive"
 
 
225
  };
226
 
227
  const response = await fetch(targetUrl, {
@@ -241,7 +240,7 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
241
  responseHeaders.delete('server');
242
  responseHeaders.delete('x-powered-by');
243
  responseHeaders.delete('cf-ray');
244
- responseHeaders.delete('access-control-allow-origin'); // Limpiamos rastros del upstream
245
 
246
  if (isImage) {
247
  const contentType = responseHeaders.get("content-type") || "";
@@ -264,8 +263,6 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
264
  }
265
  releaseSlot();
266
  responseSent = true;
267
-
268
- // Purga manual de variables
269
  payload = null;
270
 
271
  return res.status(response.status).json({
@@ -299,7 +296,6 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
299
  }
300
  }
301
 
302
- // Streaming (Texto)
303
  res.writeHead(response.status, {
304
  'Content-Type': responseHeaders.get('content-type') || 'text/event-stream',
305
  'Cache-Control': 'no-cache',
@@ -312,7 +308,7 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
312
 
313
  stream.on('end', () => {
314
  releaseSlot();
315
- payload = null; // Purga al terminar el stream
316
  });
317
  stream.on('error', () => {
318
  releaseSlot();
@@ -337,7 +333,6 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
337
  }
338
  }
339
 
340
- // Purga final de seguridad si falla todo
341
  payload = null;
342
 
343
  if (!responseSent) {
@@ -346,5 +341,5 @@ app.post(['/v1/chat/completions', '/v1/images/generations'], limiter, async (req
346
  });
347
 
348
  app.listen(PORT, '0.0.0.0', () => {
349
- console.log(`[SYS] Nivel de cifrado: Tor. Puerto: ${PORT}`);
350
  });
 
8
  const PORT = 7860;
9
 
10
  // --- 1. CONFIGURACIÓN DE SEGURIDAD PARANOICA ---
11
+ // CRÍTICO: trust proxy en 1 es vital para leer la IP real del usuario a través de Cloudflare/HF
12
  app.set('trust proxy', 1);
13
  app.disable('x-powered-by');
14
  app.use(cors());
15
 
16
  app.use((req, res, next) => {
 
17
  res.setHeader('X-Content-Type-Options', 'nosniff');
18
  res.setHeader('X-Frame-Options', 'DENY');
19
  res.setHeader('X-XSS-Protection', '1; mode=block');
20
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
21
+ res.setHeader('Referrer-Policy', 'no-referrer'); // Oculta que vienen de Ventarys AI
22
  res.setHeader('Content-Security-Policy', "default-src 'none'; frame-ancestors 'none';");
23
  next();
24
  });
 
27
  app.use(express.urlencoded({ limit: '50mb', extended: true }));
28
 
29
  function logError(providerId, reason) {
 
 
30
  console.error(`[SYSTEM] Provider Error: ${providerId} | ${reason}`);
31
  }
32
 
33
+ // Falsificación de huella digital del navegador
34
  function getRandomUserAgent() {
35
  const agents = [
36
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
 
58
  }
59
  ];
60
 
61
+ // CRÍTICO: Subimos el límite a 500 para permitir alta concurrencia.
62
+ // Ahora el proxy dejará pasar todo el tráfico y el proveedor final aplicará el rate limit por IP.
63
+ const MAX_PER_PROVIDER = 500;
64
+ const QUEUE_TIMEOUT = 45000;
65
  let currentLoad = { "pollinations-ai": 0, "kepler-cloud": 0, "voids-api": 0 };
66
 
67
  // --- RATE LIMITING LOCAL ---
 
 
68
  const limiter = rateLimit({
69
  windowMs: 60 * 1000,
70
+ max: 50, // 50 peticiones por IP localmente para evitar que alguien tire TU servidor
71
  keyGenerator: (req) => req.ip,
72
  message: { error: { message: "Too many requests.", code: 429 } },
73
+ standardHeaders: false,
74
  legacyHeaders: false,
75
  });
76
 
 
97
  method: "GET",
98
  headers: {
99
  "Content-Type": "application/json",
100
+ "User-Agent": getRandomUserAgent()
101
  }
102
  });
103
  if (!resp.ok) return [];
 
134
  }
135
 
136
  app.get('/health', (req, res) => {
 
137
  res.json({ status: "online" });
138
  });
139
 
 
179
  const startTime = Date.now();
180
  let responseSent = false;
181
 
182
+ // Extraemos la IP del cliente para reenviarla
183
+ const clientIp = req.ip || req.headers['x-forwarded-for'] || req.socket.remoteAddress;
184
+
185
  let payload = JSON.stringify(req.body);
186
+ req.body = null;
187
 
188
  while (availableProviders.length > 0 && Date.now() - startTime < QUEUE_TIMEOUT) {
189
  let selectedProvider = null;
 
213
  try {
214
  let targetUrl = isImage && selectedProvider.imageUrl ? selectedProvider.imageUrl : selectedProvider.url;
215
 
216
+ // --- OFUSCACIÓN + DELEGACIÓN DE IP ---
 
217
  const fetchHeaders = {
218
  "Content-Type": "application/json",
219
  "User-Agent": getRandomUserAgent(),
220
  "Accept": "*/*",
221
+ "Connection": "keep-alive",
222
+ "X-Forwarded-For": clientIp, // El proveedor rate-limitea esta IP
223
+ "X-Real-IP": clientIp // El proveedor rate-limitea esta IP
224
  };
225
 
226
  const response = await fetch(targetUrl, {
 
240
  responseHeaders.delete('server');
241
  responseHeaders.delete('x-powered-by');
242
  responseHeaders.delete('cf-ray');
243
+ responseHeaders.delete('access-control-allow-origin');
244
 
245
  if (isImage) {
246
  const contentType = responseHeaders.get("content-type") || "";
 
263
  }
264
  releaseSlot();
265
  responseSent = true;
 
 
266
  payload = null;
267
 
268
  return res.status(response.status).json({
 
296
  }
297
  }
298
 
 
299
  res.writeHead(response.status, {
300
  'Content-Type': responseHeaders.get('content-type') || 'text/event-stream',
301
  'Cache-Control': 'no-cache',
 
308
 
309
  stream.on('end', () => {
310
  releaseSlot();
311
+ payload = null;
312
  });
313
  stream.on('error', () => {
314
  releaseSlot();
 
333
  }
334
  }
335
 
 
336
  payload = null;
337
 
338
  if (!responseSent) {
 
341
  });
342
 
343
  app.listen(PORT, '0.0.0.0', () => {
344
+ console.log(`[SYS] Nivel de cifrado: Tor (Delegando IP). Concurrencia máxima: 500/prov. Puerto: ${PORT}`);
345
  });