From 66cbfa9629a707800a29e4f2c38da20089ab1161 Mon Sep 17 00:00:00 2001 From: vinland100 Date: Thu, 30 Oct 2025 14:58:20 +0800 Subject: [PATCH 1/2] fix: route dashscope calls through dev proxy to avoid CORS --- src/components/ui/card.tsx | 83 ++++++++++++++++++++++++++++++++++++++ vite.config.ts | 24 +++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index f4f85ac..c8372b1 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -2,6 +2,89 @@ import * as React from "react"; import { cn } from "@/shared/utils/utils"; +declare global { + interface Window { + __dashscopeProxyPatched__?: boolean; + } +} + +const DASH_SCOPE_ORIGIN = "https://dashscope.aliyuncs.com"; +const DASH_SCOPE_PROXY_PREFIX = "/dashscope-proxy"; + +// Ensure dashscope API traffic is routed through the local proxy during development to avoid browser CORS blocks. +if (typeof window !== "undefined" && import.meta.env.DEV) { + const globalWindow = window as Window; + + if (!globalWindow.__dashscopeProxyPatched__) { + const originalFetch = window.fetch.bind(window); + + const rewriteUrl = (url: string): string => { + if (url.startsWith(DASH_SCOPE_PROXY_PREFIX)) return url; + + if (typeof window !== "undefined") { + const originPrefixed = `${window.location.origin}${DASH_SCOPE_PROXY_PREFIX}`; + if (url.startsWith(originPrefixed)) { + return url.slice(window.location.origin.length); + } + } + + if (url.startsWith(DASH_SCOPE_ORIGIN)) { + return url.replace(DASH_SCOPE_ORIGIN, DASH_SCOPE_PROXY_PREFIX); + } + + return url; + }; + + const rewriteRequestInfo = (input: RequestInfo | URL): RequestInfo => { + if (typeof input === "string") { + return rewriteUrl(input); + } + + if (input instanceof URL) { + return rewriteUrl(input.toString()); + } + + const rewrittenUrl = rewriteUrl(input.url); + if (rewrittenUrl === input.url) { + return input; + } + + const cloned = input.clone(); + const init: RequestInit = { + method: cloned.method, + headers: cloned.headers, + body: + cloned.method && ["GET", "HEAD"].includes(cloned.method.toUpperCase()) + ? undefined + : cloned.body, + cache: cloned.cache, + credentials: cloned.credentials, + integrity: cloned.integrity, + keepalive: cloned.keepalive, + mode: cloned.mode, + redirect: cloned.redirect, + referrer: cloned.referrer, + referrerPolicy: cloned.referrerPolicy, + signal: cloned.signal, + }; + + return new Request(rewrittenUrl, init); + }; + + window.fetch = ((input: RequestInfo | URL, init?: RequestInit) => { + const proxiedInput = rewriteRequestInfo(input); + + if (proxiedInput instanceof Request) { + return originalFetch(proxiedInput); + } + + return originalFetch(proxiedInput, init); + }) as typeof window.fetch; + + globalWindow.__dashscopeProxyPatched__ = true; + } +} + function Card({ className, ...props }: React.ComponentProps<"div">) { return (
path.replace(/^\/dashscope-proxy/, ""), + configure: (proxy) => { + proxy.on("proxyReq", (proxyReq) => { + proxyReq.setHeader("origin", "https://dashscope.aliyuncs.com"); + }); + }, + }, + }, }, preview: { port: 5173, From ab5dfbd1f657f744c63573028a8ab30a97eeb957 Mon Sep 17 00:00:00 2001 From: vinland100 Date: Thu, 30 Oct 2025 15:41:42 +0800 Subject: [PATCH 2/2] refactor: addresses review comments, move CORS fix logic from card.tsx to qwen-adapter.js --- src/components/ui/card.tsx | 83 ------------------- .../services/llm/adapters/qwen-adapter.ts | 6 +- 2 files changed, 5 insertions(+), 84 deletions(-) diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index c8372b1..f4f85ac 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -2,89 +2,6 @@ import * as React from "react"; import { cn } from "@/shared/utils/utils"; -declare global { - interface Window { - __dashscopeProxyPatched__?: boolean; - } -} - -const DASH_SCOPE_ORIGIN = "https://dashscope.aliyuncs.com"; -const DASH_SCOPE_PROXY_PREFIX = "/dashscope-proxy"; - -// Ensure dashscope API traffic is routed through the local proxy during development to avoid browser CORS blocks. -if (typeof window !== "undefined" && import.meta.env.DEV) { - const globalWindow = window as Window; - - if (!globalWindow.__dashscopeProxyPatched__) { - const originalFetch = window.fetch.bind(window); - - const rewriteUrl = (url: string): string => { - if (url.startsWith(DASH_SCOPE_PROXY_PREFIX)) return url; - - if (typeof window !== "undefined") { - const originPrefixed = `${window.location.origin}${DASH_SCOPE_PROXY_PREFIX}`; - if (url.startsWith(originPrefixed)) { - return url.slice(window.location.origin.length); - } - } - - if (url.startsWith(DASH_SCOPE_ORIGIN)) { - return url.replace(DASH_SCOPE_ORIGIN, DASH_SCOPE_PROXY_PREFIX); - } - - return url; - }; - - const rewriteRequestInfo = (input: RequestInfo | URL): RequestInfo => { - if (typeof input === "string") { - return rewriteUrl(input); - } - - if (input instanceof URL) { - return rewriteUrl(input.toString()); - } - - const rewrittenUrl = rewriteUrl(input.url); - if (rewrittenUrl === input.url) { - return input; - } - - const cloned = input.clone(); - const init: RequestInit = { - method: cloned.method, - headers: cloned.headers, - body: - cloned.method && ["GET", "HEAD"].includes(cloned.method.toUpperCase()) - ? undefined - : cloned.body, - cache: cloned.cache, - credentials: cloned.credentials, - integrity: cloned.integrity, - keepalive: cloned.keepalive, - mode: cloned.mode, - redirect: cloned.redirect, - referrer: cloned.referrer, - referrerPolicy: cloned.referrerPolicy, - signal: cloned.signal, - }; - - return new Request(rewrittenUrl, init); - }; - - window.fetch = ((input: RequestInfo | URL, init?: RequestInit) => { - const proxiedInput = rewriteRequestInfo(input); - - if (proxiedInput instanceof Request) { - return originalFetch(proxiedInput); - } - - return originalFetch(proxiedInput, init); - }) as typeof window.fetch; - - globalWindow.__dashscopeProxyPatched__ = true; - } -} - function Card({ className, ...props }: React.ComponentProps<"div">) { return (
{