import path from 'node:path'; import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; // The reference app resolves `@/*` through the Base44 Vite plugin. This demo is // backend-free, so the alias is declared directly. // The API host the dev server forwards to. Only read here; nothing in client // code names a backend host. const PROXY_TARGET = process.env.VITE_API_PROXY_TARGET || 'https://mcp.krowforce.com'; /** * One proxy entry per path prefix, all pointing at the same backend. * * `secure: true` keeps certificate verification on — the target is a real host * with a real certificate, and turning verification off would hide a genuine * TLS failure behind a working-looking proxy. * * `changeOrigin` rewrites the Host header to the target, which the backend's * TLS termination requires to route the request at all. */ function proxyRoutes(prefixes) { return Object.fromEntries(prefixes.map((prefix) => [prefix, { target: PROXY_TARGET, changeOrigin: true, secure: true, configure: (proxy) => { proxy.on('error', (err, _req, res) => { console.error('[vite proxy error]:', err.message); if (res && res.writeHead && !res.headersSent) { res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: `Backend unreachable (${PROXY_TARGET}): ${err.message}`, })); } }); }, }])); } export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(import.meta.dirname, './src'), }, }, build: { rollupOptions: { output: { // Split the heavy libraries out of the app bundle. They change far less // often than app code, so browsers keep them cached across deploys. manualChunks: { react: ['react', 'react-dom', 'react-router-dom'], charts: ['recharts'], motion: ['framer-motion'], markdown: ['react-markdown'], canvas: ['html2canvas'], }, }, }, }, server: { // Honor a port assigned by the environment; fall back to Vite's default. port: process.env.PORT ? Number(process.env.PORT) : 5173, /** * Proxy the API so the browser only ever talks to one origin. * * This exists for the session cookie. The cookie is HttpOnly with * SameSite=Lax, and a Lax cookie is not sent on a cross-site subresource * request — which is exactly what `fetch('http://127.0.0.1:8080/...')` from * a page served by `http://localhost:5173` is, because a browser treats * those two as different sites. Every request after login would arrive * without the cookie and be answered 401. * * The alternatives are both worse. SameSite=None would send the cookie on * genuine cross-site requests too, which is the CSRF hole Lax closes, and * it requires Secure — so it cannot work over plain HTTP on localhost at * all. Widening CORS with credentials would keep the cross-site problem and * add a second one. * * browser → localhost:5173/api/v1 → this proxy → 127.0.0.1:8080/api/v1 * * Same origin from the browser's point of view, so the cookie is a * first-party cookie, CORS never enters into it, and nothing in the React * code names a backend host. * * Production serves the built assets and the API from one origin (see * nginx.conf), so this is a development-only shim for a property the * deployed app has for free. */ proxy: proxyRoutes(['/api', '/health']), }, });