The Vite dev proxy had no `ws: true` on /api, so the in-app messages socket (/api/messages/ws) was an upgrade nobody handled: it never connected on :5173, and the first reset on it (a closed tab, a dropped connection) threw an unhandled ECONNRESET that took the whole dev server down. Reproduced with a raw upgrade + RST (dev server dead); with `ws: true` the backend answers the upgrade, the dev server survives the reset and a closed tab, and the signed-in socket is accepted through the proxy.
27 lines
1.3 KiB
TypeScript
27 lines
1.3 KiB
TypeScript
import { defineConfig } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
|
|
// During `vite dev` (host with Node), proxy API calls to the backend container.
|
|
// Use 127.0.0.1 (not "localhost") so Node doesn't resolve to IPv6 ::1, which the
|
|
// Docker port publish may not answer — that surfaces as proxy connection failures.
|
|
const target = "http://127.0.0.1:8080";
|
|
// changeOrigin:false keeps the original `Host: localhost:5173` header when proxying to the backend
|
|
// (the TCP target is still 127.0.0.1:8080 — changeOrigin only rewrites the header). The backend uses
|
|
// that header to send the OAuth redirect_uri back to :5173, so a Google login started on the Vite dev
|
|
// server returns to :5173 instead of the fixed :8080 callback. (Both are registered in the console.)
|
|
const proxyOpts = { target, changeOrigin: false };
|
|
const proxy = {
|
|
// ws: the in-app messages socket (/api/messages/ws) is a WebSocket. Without it Vite hands that upgrade
|
|
// to nobody, so the socket never connects on :5173 — and the first reset on it (a closed tab, a dropped
|
|
// connection) throws an unhandled ECONNRESET that takes the whole dev server down.
|
|
"/api": { ...proxyOpts, ws: true },
|
|
"/auth": proxyOpts,
|
|
"/healthz": proxyOpts,
|
|
};
|
|
|
|
export default defineConfig({
|
|
plugins: [react()],
|
|
server: { port: 5173, proxy },
|
|
build: { outDir: "dist" },
|
|
});
|