import { defineConfig, loadEnv } from 'vite' import react from '@vitejs/plugin-react' /** * Dev runner for /api/* requests in `npm run dev`. * * The files in /api are Vercel serverless functions — on Vercel they run as * real functions; locally Vite doesn't execute them. To test the real APIs * locally (e.g. the Tomorrow.io forecast) this middleware loads .env into * process.env and invokes the ACTUAL handler for KV-free routes via a small * req/res shim that matches the Vercel handler contract. * * Routes that use server-only deps (`@vercel/kv`: hail-status, hail-webhook) * stay stubbed with a 503 — every frontend hook that hits these already * degrades gracefully on a failed fetch. */ // Only KV-free handlers are safe to import into the dev pipeline. const DEV_API_HANDLERS = { 'storm-forecast': () => import('./api/storm-forecast.js'), 'hail-proxy': () => import('./api/hail-proxy.js'), 'storm-events': () => import('./api/storm-events.js'), 'storm-polygons': () => import('./api/storm-polygons.js'), } // Server-side env vars the handlers read (mirrors what you'd set in Vercel). const SERVER_ENV_KEYS = [ 'TOMORROW_API_KEY', 'HAIL_RECON_ACCESS_KEY', 'HAIL_RECON_ACCESS_SECRET', 'HAIL_RECON_WEBHOOK_SECRET', ] function devApi(env) { for (const k of SERVER_ENV_KEYS) { if (env[k] && !process.env[k]) process.env[k] = env[k] } return { name: 'dev-api', apply: 'serve', configureServer(server) { server.middlewares.use(async (req, res, next) => { if (!req.url || !req.url.startsWith('/api/')) return next() const url = new URL(req.url, 'http://localhost') const name = url.pathname.replace(/^\/api\//, '').replace(/\/+$/, '') const load = DEV_API_HANDLERS[name] if (!load) { res.statusCode = 503 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ error: 'API route not available in local dev (needs Vercel).' })) return } // Shim the Node req/res to the Vercel handler contract. req.query = Object.fromEntries(url.searchParams.entries()) res.status = (code) => { res.statusCode = code; return res } res.json = (obj) => { if (!res.getHeader('Content-Type')) res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify(obj)) return res } try { const mod = await load() await mod.default(req, res) } catch (e) { res.statusCode = 500 res.setHeader('Content-Type', 'application/json') res.end(JSON.stringify({ error: 'dev api handler failed', detail: e.message })) } }) }, } } export default defineConfig(({ mode }) => { // Empty prefix → load ALL vars from .env (including non-VITE_ server keys). const env = loadEnv(mode, process.cwd(), '') return { plugins: [react(), devApi(env)], server: { // Don't watch/serve /api as client files — they're invoked server-side above. watch: { ignored: ['**/api/**'] }, fs: { deny: ['api'] }, }, optimizeDeps: { exclude: ['api'], }, } })