diff --git a/vite.config.mts b/vite.config.mts index 96f101256..cffc21408 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -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,