chore(vite.config): add GCS redirect for assets (#6389)

## Summary

Add GCS redirect in vite's proxy so we can load workflows by dragging
images in local cloud development.

Previously this action would result in a CORS error when the app tried
to download from `storage.googleapis.com`

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6389-chore-vite-config-add-GCS-redirect-for-assets-29c6d73d36508148aaabd24de9698fc4)
by [Unito](https://www.unito.io)
This commit is contained in:
Arjan Singh
2025-10-29 19:34:38 -07:00
committed by GitHub
parent fac8bd68dc
commit 09143c05c1

View File

@@ -1,13 +1,16 @@
import tailwindcss from '@tailwindcss/vite'
import vue from '@vitejs/plugin-vue'
import { config as dotenvConfig } from 'dotenv'
import type { IncomingMessage, ServerResponse } from 'http'
import { Readable } from 'stream'
import type { ReadableStream as NodeReadableStream } from 'stream/web'
import { visualizer } from 'rollup-plugin-visualizer'
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
import IconsResolver from 'unplugin-icons/resolver'
import Icons from 'unplugin-icons/vite'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import type { UserConfig } from 'vite'
import type { ProxyOptions, UserConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import vueDevTools from 'vite-plugin-vue-devtools'
@@ -49,10 +52,74 @@ const DEV_SEVER_FALLBACK_URL =
const DEV_SERVER_COMFYUI_URL =
DEV_SERVER_COMFYUI_ENV_URL || DEV_SEVER_FALLBACK_URL
// Cloud proxy configuration
const cloudProxyConfig =
DISTRIBUTION === 'cloud' ? { secure: false, changeOrigin: true } : {}
function handleGcsRedirect(
proxyRes: IncomingMessage,
_req: IncomingMessage,
res: ServerResponse
) {
const location = proxyRes.headers.location
const isGcsRedirect =
proxyRes.statusCode === 302 &&
location?.includes('storage.googleapis.com') &&
proxyRes.headers.via?.includes('google')
// Not a GCS redirect - pass through normally
if (!isGcsRedirect || !location) {
Object.keys(proxyRes.headers).forEach((key) => {
const value = proxyRes.headers[key]
if (value !== undefined) {
res.setHeader(key, value)
}
})
res.writeHead(proxyRes.statusCode || 200)
proxyRes.pipe(res)
return
}
// GCS redirect detected - fetch server-side to avoid CORS
fetch(location)
.then(async (gcsResponse) => {
if (!gcsResponse.body) {
res.statusCode = 500
res.end('Empty response from GCS')
return
}
// Set response headers from GCS
res.statusCode = 200
res.setHeader(
'Content-Type',
gcsResponse.headers.get('content-type') || 'application/octet-stream'
)
const contentLength = gcsResponse.headers.get('content-length')
if (contentLength) {
res.setHeader('Content-Length', contentLength)
}
// Convert Web ReadableStream to Node.js stream and pipe to client
const readable = Readable.fromWeb(gcsResponse.body as NodeReadableStream)
readable.pipe(res)
})
.catch((error) => {
console.error('Error fetching from GCS:', error)
res.statusCode = 500
res.end('Error fetching media')
})
}
const gcsRedirectProxyConfig: ProxyOptions = {
target: DEV_SERVER_COMFYUI_URL,
...cloudProxyConfig,
selfHandleResponse: true,
configure: (proxy) => {
proxy.on('proxyRes', handleGcsRedirect)
}
}
export default defineConfig({
base: '',
server: {
@@ -80,6 +147,13 @@ export default defineConfig({
...cloudProxyConfig
},
...(DISTRIBUTION === 'cloud'
? {
'/api/view': gcsRedirectProxyConfig,
'/api/viewvideo': gcsRedirectProxyConfig
}
: {}),
'/api': {
target: DEV_SERVER_COMFYUI_URL,
...cloudProxyConfig,