Files
LynkedUpPro_CRM/vite.config.js
T
2026-06-12 20:49:34 +05:30

118 lines
4.4 KiB
JavaScript

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'),
'storm-history': () => import('./api/storm-history.js'),
'hail-proxy': () => import('./api/hail-proxy.js'),
'storm-events': () => import('./api/storm-events.js'),
'storm-polygons': () => import('./api/storm-polygons.js'),
// Payments module (needs real Stripe test keys in .env to function locally).
// The webhook is intentionally excluded — it needs the raw body + Stripe CLI.
'payments/create-checkout-session': () => import('./api/payments/create-checkout-session.js'),
'payments/session-status': () => import('./api/payments/session-status.js'),
'payments/transactions': () => import('./api/payments/transactions.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',
'STRIPE_SECRET_KEY',
'STRIPE_WEBHOOK_SECRET',
]
function devApi(env) {
// .env is authoritative in dev: always apply it so edits are picked up on a
// server restart. (A `!process.env[k]` guard here would keep a stale value
// from a previous load — e.g. the placeholder secret key — and silently
// ignore the real key you just added.)
for (const k of SERVER_ENV_KEYS) {
if (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
}
res.send = (body) => { res.end(typeof body === 'string' ? body : JSON.stringify(body)); return res }
// Parse a JSON body for POST/PUT/PATCH (Vercel does this automatically).
if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
req.body = await new Promise((resolve) => {
let data = ''
req.on('data', (c) => { data += c })
req.on('end', () => {
try { resolve(data ? JSON.parse(data) : {}) } catch { resolve({}) }
})
req.on('error', () => resolve({}))
})
}
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'],
},
}
})