Compare commits

..

4 Commits

Author SHA1 Message Date
Stephen Zhou
f530b1647b fix 2026-03-12 23:02:34 +08:00
autofix-ci[bot]
5b300aa5c9 [autofix.ci] apply automated fixes 2026-03-12 14:53:46 +00:00
Stephen Zhou
4985e245d4 fix css 2026-03-12 22:50:52 +08:00
Stephen Zhou
bfe11c89cf chore: add vite proxy config 2026-03-12 22:19:52 +08:00
6 changed files with 142 additions and 8 deletions

View File

@@ -12,6 +12,9 @@ NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
# Dev-only Vite proxy targets. Use these when routing same-origin requests through the local dev server.
VITE_CONSOLE_API_PROXY_TARGET=https://cloud.dify.ai
VITE_PUBLIC_API_PROXY_TARGET=https://cloud.dify.ai
# When the frontend and backend run on different subdomains, set NEXT_PUBLIC_COOKIE_DOMAIN=1.
NEXT_PUBLIC_COOKIE_DOMAIN=

View File

@@ -4,7 +4,6 @@ import { cva } from 'class-variance-authority'
import * as React from 'react'
import { cn } from '@/utils/classnames'
import Divider from '../divider'
import './index.css'
type SegmentedControlOption<T> = {
value: T
@@ -131,7 +130,7 @@ export const SegmentedControl = <T extends string | number | symbol>({
<div className={cn('inline-flex items-center gap-x-1', ItemTextWrapperVariants({ size }))}>
<span>{text}</span>
{!!(count && size === 'large') && (
<div className="system-2xs-medium-uppercase inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] text-text-tertiary">
<div className="inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] text-text-tertiary system-2xs-medium-uppercase">
{count}
</div>
)}

View File

@@ -11,6 +11,7 @@
@import "../components/base/button/index.css";
@import "../components/base/modal/index.css";
@import "../components/base/premium-badge/index.css";
@import "../components/base/segmented-control/index.css";
@tailwind base;
@tailwind components;

View File

@@ -2657,11 +2657,6 @@
"count": 1
}
},
"app/components/base/segmented-control/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
},
"app/components/base/select/custom.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2

130
web/plugins/vite/proxy.ts Normal file
View File

@@ -0,0 +1,130 @@
import type { ProxyOptions } from 'vite'
const DEFAULT_PROXY_TARGET = 'https://cloud.dify.ai'
const SECURE_COOKIE_PREFIX_PATTERN = /^__(Host|Secure)-/
const SAME_SITE_NONE_PATTERN = /^samesite=none$/i
const COOKIE_PATH_PATTERN = /^path=/i
const COOKIE_DOMAIN_PATTERN = /^domain=/i
const COOKIE_SECURE_PATTERN = /^secure$/i
const COOKIE_PARTITIONED_PATTERN = /^partitioned$/i
const HOST_PREFIX_COOKIE_NAMES = new Set([
'access_token',
'csrf_token',
'refresh_token',
'webapp_access_token',
])
const isPassportCookie = (cookieName: string) => cookieName.startsWith('passport-')
const shouldUseHostPrefix = (cookieName: string) => {
const normalizedCookieName = cookieName.replace(SECURE_COOKIE_PREFIX_PATTERN, '')
return HOST_PREFIX_COOKIE_NAMES.has(normalizedCookieName) || isPassportCookie(normalizedCookieName)
}
const toCloudCookieName = (cookieName: string) => {
if (cookieName.startsWith('__Host-'))
return cookieName
if (cookieName.startsWith('__Secure-'))
return `__Host-${cookieName.replace(SECURE_COOKIE_PREFIX_PATTERN, '')}`
if (!shouldUseHostPrefix(cookieName))
return cookieName
return `__Host-${cookieName}`
}
const toLocalCookieName = (cookieName: string) => cookieName.replace(SECURE_COOKIE_PREFIX_PATTERN, '')
export const rewriteCookieHeaderForCloud = (cookieHeader?: string) => {
if (!cookieHeader)
return cookieHeader
return cookieHeader
.split(/;\s*/)
.filter(Boolean)
.map((cookie) => {
const separatorIndex = cookie.indexOf('=')
if (separatorIndex === -1)
return cookie
const cookieName = cookie.slice(0, separatorIndex).trim()
const cookieValue = cookie.slice(separatorIndex + 1)
return `${toCloudCookieName(cookieName)}=${cookieValue}`
})
.join('; ')
}
const rewriteSetCookieValueForLocal = (setCookieValue: string) => {
const [rawCookiePair, ...rawAttributes] = setCookieValue.split(';')
const separatorIndex = rawCookiePair.indexOf('=')
if (separatorIndex === -1)
return setCookieValue
const cookieName = rawCookiePair.slice(0, separatorIndex).trim()
const cookieValue = rawCookiePair.slice(separatorIndex + 1)
const rewrittenAttributes = rawAttributes
.map(attribute => attribute.trim())
.filter(attribute =>
!COOKIE_DOMAIN_PATTERN.test(attribute)
&& !COOKIE_SECURE_PATTERN.test(attribute)
&& !COOKIE_PARTITIONED_PATTERN.test(attribute),
)
.map((attribute) => {
if (SAME_SITE_NONE_PATTERN.test(attribute))
return 'SameSite=Lax'
if (COOKIE_PATH_PATTERN.test(attribute))
return 'Path=/'
return attribute
})
return [`${toLocalCookieName(cookieName)}=${cookieValue}`, ...rewrittenAttributes].join('; ')
}
export const rewriteSetCookieHeadersForLocal = (setCookieHeaders?: string | string[]): string[] | undefined => {
if (!setCookieHeaders)
return undefined
const normalizedHeaders = Array.isArray(setCookieHeaders)
? setCookieHeaders
: [setCookieHeaders]
return normalizedHeaders.map(rewriteSetCookieValueForLocal)
}
const createCookieAwareProxy = (target = DEFAULT_PROXY_TARGET): ProxyOptions => ({
target,
changeOrigin: true,
secure: true,
configure(proxy) {
proxy.on('proxyReq', (proxyReq, request) => {
const cookieHeader = rewriteCookieHeaderForCloud(request.headers.cookie)
if (cookieHeader)
proxyReq.setHeader('cookie', cookieHeader)
})
proxy.on('proxyRes', (proxyRes) => {
const rewrittenSetCookieHeaders = rewriteSetCookieHeadersForLocal(proxyRes.headers['set-cookie'])
if (rewrittenSetCookieHeaders)
proxyRes.headers['set-cookie'] = rewrittenSetCookieHeaders
})
},
})
export const createDevProxyConfig = (options?: {
consoleApiTarget?: string
publicApiTarget?: string
}) => {
const consoleApiTarget = options?.consoleApiTarget || DEFAULT_PROXY_TARGET
const publicApiTarget = options?.publicApiTarget || consoleApiTarget
return {
'/console/api': createCookieAwareProxy(consoleApiTarget),
'/api': createCookieAwareProxy(publicApiTarget),
} satisfies Record<string, ProxyOptions>
}

View File

@@ -2,17 +2,19 @@ import path from 'node:path'
import { fileURLToPath } from 'node:url'
import react from '@vitejs/plugin-react'
import vinext from 'vinext'
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'
import Inspect from 'vite-plugin-inspect'
import tsconfigPaths from 'vite-tsconfig-paths'
import { createCodeInspectorPlugin, createForceInspectorClientInjectionPlugin } from './plugins/vite/code-inspector'
import { customI18nHmrPlugin } from './plugins/vite/custom-i18n-hmr'
import { createDevProxyConfig } from './plugins/vite/proxy'
const projectRoot = path.dirname(fileURLToPath(import.meta.url))
const isCI = !!process.env.CI
const browserInitializerInjectTarget = path.resolve(projectRoot, 'app/components/browser-initializer.tsx')
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, projectRoot, '')
const isTest = mode === 'test'
const isStorybook = process.env.STORYBOOK === 'true'
|| process.argv.some(arg => arg.toLowerCase().includes('storybook'))
@@ -65,6 +67,10 @@ export default defineConfig(({ mode }) => {
},
server: {
port: 3000,
proxy: createDevProxyConfig({
consoleApiTarget: env.VITE_CONSOLE_API_PROXY_TARGET,
publicApiTarget: env.VITE_PUBLIC_API_PROXY_TARGET,
}),
},
ssr: {
// SyntaxError: Named export not found. The requested module is a CommonJS module, which may not support all module.exports as named exports