@@ -8,7 +8,7 @@ import tailwindcss from '@tailwindcss/vite'
88import { tanstackRouter } from '@tanstack/router-plugin/vite'
99import viteReact from '@vitejs/plugin-react'
1010import { playwright } from '@vitest/browser-playwright'
11- import { loadEnv , type Plugin } from 'vite'
11+ import { loadEnv , type Plugin , type ProxyOptions } from 'vite'
1212import svgr from 'vite-plugin-svgr'
1313import { defineConfig } from 'vitest/config'
1414
@@ -73,26 +73,63 @@ const TESTED_TSX_FILES = [
7373// local .env file. For release builds, env.json is written to dist/ and contains
7474// defaults (from .env.example) which can be overridden at deploy time (e.g. via
7575// env vars on the Docker container; see docker/90-write-env-to-json.sh)
76+ //
77+ // In dev mode the backend is proxied through the dev server, and env.json points
78+ // VITE_API_BASE_URL at the dev server itself. This keeps the backend's session
79+ // cookie first-party, which a SameSite=Lax cookie needs when the backend is
80+ // remote (e.g. developing against production).
7681function runtimeEnv ( ) : Plugin {
77- let json : string
82+ let env : Record < string , string >
7883 return {
7984 name : 'maproulette:runtime-env' ,
80- configResolved ( config ) {
81- json = JSON . stringify ( loadEnv ( config . mode , config . root , 'VITE_' ) , null , 2 )
85+ config ( config , { mode, command } ) {
86+ env = loadEnv ( mode , resolve ( config . root ?? process . cwd ( ) ) , 'VITE_' )
87+ if ( command !== 'serve' ) return
88+ const target = env . VITE_API_BASE_URL || 'http://127.0.0.1:9000'
89+ const http : ProxyOptions = {
90+ target,
91+ changeOrigin : true ,
92+ cookieDomainRewrite : '' ,
93+ configure : ( proxy ) => {
94+ // The backend derives the OAuth redirect_uri from Origin, falling
95+ // back to its configured frontend. Browsers omit Origin on
96+ // same-origin GETs, so supply the dev server's.
97+ proxy . on ( 'proxyReq' , ( proxyReq , req ) => {
98+ if ( ! req . headers . origin ) proxyReq . setHeader ( 'origin' , `http://${ req . headers . host } ` )
99+ } )
100+ // The dev server is plain http, where browsers may drop Secure cookies.
101+ proxy . on ( 'proxyRes' , ( res ) => {
102+ res . headers [ 'set-cookie' ] = res . headers [ 'set-cookie' ] ?. map ( ( c ) =>
103+ c . replace ( / ; \s * S e c u r e / i, '' )
104+ )
105+ } )
106+ } ,
107+ }
108+ return {
109+ server : {
110+ proxy : {
111+ '/api' : http ,
112+ '/auth' : http ,
113+ '/ws' : { target, changeOrigin : true , ws : true } ,
114+ } ,
115+ } ,
116+ }
82117 } ,
83118 configureServer ( server ) {
84119 server . middlewares . use ( ( req , res , next ) => {
85120 if ( req . url !== '/env.json' ) return next ( )
86121 res . setHeader ( 'Content-Type' , 'application/json' )
87122 res . setHeader ( 'Cache-Control' , 'no-store' )
88- res . end ( json )
123+ res . end (
124+ JSON . stringify ( { ...env , VITE_API_BASE_URL : `http://${ req . headers . host } ` } , null , 2 )
125+ )
89126 } )
90127 } ,
91128 generateBundle ( ) {
92129 this . emitFile ( {
93130 type : 'asset' ,
94131 fileName : 'env.json' ,
95- source : json ,
132+ source : JSON . stringify ( env , null , 2 ) ,
96133 } )
97134 } ,
98135 }
0 commit comments