import { defineConfig, loadEnv } from "vite"; const MAX_BATCH_BYTES = 96 * 1024; export async function forwardMarketBatch(body, { url, token, fetchImpl = fetch }) { if (!url || !token) return new Response("Market import is not configured", { status: 503 }); return fetchImpl(url, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, body, redirect: "error", signal: AbortSignal.timeout(15_000), }); } export function rejectNonLocalRequest(request) { const rawContentType = request.headers["content-type"]; const contentType = typeof rawContentType === "string" ? rawContentType.split(";", 1)[0].trim().toLowerCase() : ""; if (contentType !== "application/json") return 415; const host = typeof request.headers.host === "string" ? request.headers.host : ""; return request.headers.origin === `http://${host}` ? null : 403; } function marketImportProxy(options) { return { name: "market-import-proxy", configureServer(server) { server.middlewares.use("/api/market-import", async (request, response) => { if (request.method !== "POST") { response.statusCode = 405; response.end(); return; } const rejection = rejectNonLocalRequest(request); if (rejection) { response.statusCode = rejection; response.end(); return; } try { const body = await readBody(request); const upstream = await forwardMarketBatch(body, options); response.statusCode = upstream.status; response.setHeader("Content-Type", upstream.headers.get("content-type") || "application/json"); response.end(await upstream.text()); } catch (error) { response.statusCode = error.code === "BATCH_TOO_LARGE" ? 413 : 502; response.end(error.code === "BATCH_TOO_LARGE" ? "Batch too large" : "Market import failed"); } }); }, }; } function readBody(request) { return new Promise((resolve, reject) => { const chunks = []; let size = 0; let tooLarge = false; request.on("data", (chunk) => { if (tooLarge) return; size += chunk.length; if (size > MAX_BATCH_BYTES) { tooLarge = true; const error = new Error("Batch too large"); error.code = "BATCH_TOO_LARGE"; reject(error); return; } chunks.push(chunk); }); request.on("end", () => { if (!tooLarge) resolve(Buffer.concat(chunks).toString("utf8")); }); request.on("error", reject); }); } export default defineConfig(({ mode }) => { const environment = loadEnv(mode, process.cwd(), ""); return { plugins: [ marketImportProxy({ url: environment.L2_MARKET_IMPORT_URL, token: environment.L2_MARKET_IMPORT_TOKEN, }), ], }; });