You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			83 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			83 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			TypeScript
		
	
| const BOT_ORIGIN = 'http://127.0.0.1:3007';
 | |
| const WEB_ORIGIN = 'http://127.0.0.1:3008';
 | |
| 
 | |
| function shouldRouteToBot(pathname: string): boolean {
 | |
|   if (pathname === '/metrics' || pathname.startsWith('/metrics/')) return true;
 | |
|   if (pathname === '/webhook' || pathname.startsWith('/webhook/')) return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| function buildForwardHeaders(req: Request): Headers {
 | |
|   const headers = new Headers(req.headers);
 | |
|   try {
 | |
|     const proto = headers.get('x-forwarded-proto') || 'https';
 | |
|     const fwdFor = headers.get('x-forwarded-for');
 | |
|     headers.set('x-forwarded-proto', proto);
 | |
|     headers.set('x-forwarded-for', fwdFor ? `${fwdFor}, 127.0.0.1` : '127.0.0.1');
 | |
|     const host = headers.get('host') || '';
 | |
|     if (!host) headers.set('host', 'localhost');
 | |
|   } catch {}
 | |
|   return headers;
 | |
| }
 | |
| 
 | |
| Bun.serve({
 | |
|   port: Number(process.env.PORT || process.env.ROUTER_PORT || 3000),
 | |
|   fetch: async (req) => {
 | |
|     const url = new URL(req.url);
 | |
| 
 | |
|     // Health local para el contenedor (evita 404 en healthcheck)
 | |
|     if (url.pathname === '/health') {
 | |
|       return new Response('ok', { status: 200, headers: { 'content-type': 'text/plain' } });
 | |
|     }
 | |
| 
 | |
|     const routeToBot = shouldRouteToBot(url.pathname);
 | |
|     const targetOrigin = routeToBot ? BOT_ORIGIN : WEB_ORIGIN;
 | |
|     const targetUrl = targetOrigin + url.pathname + url.search;
 | |
| 
 | |
|     const headers = buildForwardHeaders(req);
 | |
|     if (!routeToBot) {
 | |
|       try { headers.set('accept-encoding', 'identity'); } catch {}
 | |
|     }
 | |
|     const init: RequestInit = {
 | |
|       method: req.method,
 | |
|       headers,
 | |
|       body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req.body,
 | |
|       redirect: 'manual',
 | |
|     };
 | |
| 
 | |
|     const started = Date.now();
 | |
|     try {
 | |
|       const res = await fetch(targetUrl, init);
 | |
|       const ms = Date.now() - started;
 | |
|       try {
 | |
|         console.log(`[proxy] ${req.method} ${url.pathname}${url.search} -> ${routeToBot ? 'bot' : 'web'} ${res.status} (${ms}ms)`);
 | |
|       } catch {}
 | |
|       // Devuelve la respuesta (incluye Set-Cookie, Location, etc.), asegurando Content-Type en assets por si faltase
 | |
|       const passthroughHeaders = new Headers(res.headers);
 | |
|       if (!routeToBot) {
 | |
|         try {
 | |
|           // Forzar respuesta sin compresión hacia el cliente
 | |
|           passthroughHeaders.delete('content-encoding');
 | |
|           passthroughHeaders.delete('vary');
 | |
|           passthroughHeaders.delete('content-length');
 | |
|           const cc = passthroughHeaders.get('cache-control');
 | |
|           if (cc && !/no-transform/i.test(cc)) {
 | |
|             passthroughHeaders.set('cache-control', cc + ', no-transform');
 | |
|           } else if (!cc) {
 | |
|             passthroughHeaders.set('cache-control', 'no-transform');
 | |
|           }
 | |
|         } catch {}
 | |
|       }
 | |
|       if (!passthroughHeaders.get('content-type')) {
 | |
|         if (url.pathname.endsWith('.js')) passthroughHeaders.set('content-type', 'application/javascript; charset=utf-8');
 | |
|         if (url.pathname.endsWith('.css')) passthroughHeaders.set('content-type', 'text/css; charset=utf-8');
 | |
|       }
 | |
|       return new Response(res.body, { status: res.status, headers: passthroughHeaders });
 | |
|     } catch (err) {
 | |
|       const msg = err instanceof Error ? err.message : String(err);
 | |
|       console.error(`[proxy] ${req.method} ${url.pathname}${url.search} -> ERROR: ${msg}`);
 | |
|       return new Response(`Proxy error: ${msg}\n`, { status: 502 });
 | |
|     }
 | |
|   },
 | |
| });
 |