diff --git a/Dockerfile b/Dockerfile index 7d055b6..f3fcaea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,20 +10,35 @@ ENV DATA_DIR=/app/data # Create data directory with proper permissions RUN mkdir -p /app/data && chown -R bun:bun /app/data -# Install dependencies first (better layer caching) +# Install bot dependencies first (better layer caching) COPY package.json bun.lock ./ RUN bun install -# Copy only necessary files +# Prepare and install web dependencies +COPY apps/web/package.json apps/web/ +WORKDIR /app/apps/web +RUN bun install + +# Copy sources +WORKDIR /app COPY src/ ./src/ COPY index.ts ./ +COPY apps/web/ /app/apps/web/ +COPY proxy.ts ./ + +# Build the web app +WORKDIR /app/apps/web +RUN bun run build + +# Return to root workdir +WORKDIR /app -# More forgiving health check during debugging +# More forgiving health check during debugging (router en 3000) HEALTHCHECK --start-period=30s --interval=30s --timeout=3s --retries=3 \ - CMD curl -f http://localhost:${PORT:-3007}/health || exit 0 + CMD curl -f http://localhost:3000/health || exit 0 -# Server runs on port from environment variable -EXPOSE ${PORT:-3007} +# Expose router port +EXPOSE 3000 # Declare volume for persistent data by default VOLUME ["/app/data"] diff --git a/proxy.ts b/proxy.ts new file mode 100644 index 0000000..1b2fa39 --- /dev/null +++ b/proxy.ts @@ -0,0 +1,44 @@ +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'); + } catch {} + return headers; +} + +Bun.serve({ + port: Number(process.env.PORT || process.env.ROUTER_PORT || 3000), + fetch: async (req) => { + const url = new URL(req.url); + const targetOrigin = shouldRouteToBot(url.pathname) ? BOT_ORIGIN : WEB_ORIGIN; + const targetUrl = targetOrigin + url.pathname + url.search; + + const init: RequestInit = { + method: req.method, + headers: buildForwardHeaders(req), + body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req.body, + redirect: 'manual', + }; + + try { + const res = await fetch(targetUrl, init); + // Devuelve la respuesta tal cual (incluye Set-Cookie, Location, etc.) + return res; + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + return new Response(`Proxy error: ${msg}\n`, { status: 502 }); + } + }, +}); diff --git a/startup.sh b/startup.sh index 1610d46..f188e53 100644 --- a/startup.sh +++ b/startup.sh @@ -1,7 +1,18 @@ #!/bin/bash +set -euo pipefail -# Wait for server to be ready +# Arranca el bot en segundo plano (puerto 3007 por defecto) +BOT_PORT="${BOT_PORT:-3007}" +PORT="$BOT_PORT" bun run index.ts & + +# Arranca la web (SvelteKit) en segundo plano en el puerto 3008 +WEB_PORT="${WEB_PORT:-3008}" +pushd apps/web >/dev/null +PORT="$WEB_PORT" bun ./build/index.js & +popd >/dev/null + +# Pequeña espera para evitar condiciones de carrera sleep 1 -# Start the main process -exec bun run index.ts +# Arranca el router en primer plano en el puerto 3000 (o $PORT si viene de CapRover) +exec bun proxy.ts