fear: admin添加app应用版本管理

system-features添加
This commit is contained in:
npc0-hue
2026-02-09 09:11:58 +08:00
parent 8c9e7652ec
commit df9bed2950
35 changed files with 1656 additions and 40 deletions
+2 -2
View File
@@ -7,11 +7,11 @@ NEXT_PUBLIC_BASE_PATH=
# The base URL of console application, refers to the Console base URL of WEB service if console domain is
# different from api or web app domain.
# example: http://cloud.dify.ai/console/api
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
NEXT_PUBLIC_API_PREFIX=http://localhost:3000/console/api
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:3000/api
# When the frontend and backend run on different subdomains, set NEXT_PUBLIC_COOKIE_DOMAIN=1.
NEXT_PUBLIC_COOKIE_DOMAIN=
+23 -2
View File
@@ -53,16 +53,37 @@ const getStringConfig = (
return defaultValue
}
export const API_PREFIX = getStringConfig(
/**
* 在浏览器中,若配置的 API 地址为 localhost 或 127.0.0.1,则改为使用当前页面的 origin,
* 避免 127.0.0.1 与 localhost 不同源导致 cookie 无法携带(登录后 /api/login/status 拿不到 console 的 access_token)。
*/
function normalizeSameOriginApiPrefix(raw: string): string {
if (typeof globalThis.window === 'undefined' || !raw.startsWith('http'))
return raw
try {
const u = new URL(raw)
if (u.hostname === 'localhost' || u.hostname === '127.0.0.1')
return globalThis.window.location.origin + (u.pathname.replace(/\/$/, '') || '/')
return raw
}
catch {
return raw
}
}
const _apiPrefixRaw = getStringConfig(
process.env.NEXT_PUBLIC_API_PREFIX,
DatasetAttr.DATA_API_PREFIX,
'http://localhost:5001/console/api',
)
export const PUBLIC_API_PREFIX = getStringConfig(
const _publicApiPrefixRaw = getStringConfig(
process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
DatasetAttr.DATA_PUBLIC_API_PREFIX,
'http://localhost:5001/api',
)
export const API_PREFIX = normalizeSameOriginApiPrefix(_apiPrefixRaw)
export const PUBLIC_API_PREFIX = normalizeSameOriginApiPrefix(_publicApiPrefixRaw)
export const MARKETPLACE_API_PREFIX = getStringConfig(
process.env.NEXT_PUBLIC_MARKETPLACE_API_PREFIX,
DatasetAttr.DATA_MARKETPLACE_API_PREFIX,
+6 -2
View File
@@ -4,7 +4,7 @@ import type { SystemFeatures } from '@/types/feature'
import { useQuery } from '@tanstack/react-query'
import { create } from 'zustand'
import Loading from '@/app/components/base/loading'
import { consoleClient } from '@/service/client'
import { consoleClient, setLoginConfigToken } from '@/service/client'
import { defaultSystemFeatures } from '@/types/feature'
import { fetchSetupStatusWithCache } from '@/utils/setup-status'
@@ -21,8 +21,12 @@ export const useGlobalPublicStore = create<GlobalPublicStore>(set => ({
const systemFeaturesQueryKey = ['systemFeatures'] as const
const setupStatusQueryKey = ['setupStatus'] as const
// extend: CVE-2025-63387未授权访问 — 先请求 bootstrap 拿 JWTcookie + body),跨域时用 Header 带 token 请求 login_config
async function fetchSystemFeatures() {
const data = await consoleClient.systemFeatures()
const bootstrapRes = await consoleClient.loginConfigBootstrap()
if (bootstrapRes?.token)
setLoginConfigToken(bootstrapRes.token)
const data = await consoleClient.loginConfig()
const { setSystemFeatures } = useGlobalPublicStore.getState()
setSystemFeatures({ ...defaultSystemFeatures, ...data })
return data
+12 -2
View File
@@ -2,9 +2,19 @@ import type { SystemFeatures } from '@/types/feature'
import { type } from '@orpc/contract'
import { base } from '../base'
export const systemFeaturesContract = base
// extend: CVE-2025-63387未授权访问 虽然这个api实际上就是个登录用的
export const loginConfigBootstrapContract = base
.route({
path: '/system-features',
path: '/login_config_bootstrap',
method: 'GET',
})
.input(type<unknown>())
.output(type<{ ok: boolean, token: string }>())
// extend: CVE-2025-63387未授权访问 虽然这个api实际上就是个登录用的 — 路径改为 login_config,需先请求 login_config_bootstrap 写入 cookie
export const loginConfigContract = base
.route({
path: '/login_config',
method: 'GET',
})
.input(type<unknown>())
+5 -2
View File
@@ -1,6 +1,7 @@
import type { InferContractRouterInputs } from '@orpc/contract'
import { bindPartnerStackContract, invoicesContract } from './console/billing'
import { systemFeaturesContract } from './console/system'
// extend: CVE-2025-63387未授权访问 虽然这个api实际上就是个登录用的 — 路径改为 login_config,需先请求 login_config_bootstrap 写入 cookie
import { loginConfigBootstrapContract, loginConfigContract } from './console/system'
import { trialAppDatasetsContract, trialAppInfoContract, trialAppParametersContract, trialAppWorkflowsContract } from './console/try-app'
import { collectionPluginsContract, collectionsContract, searchAdvancedContract } from './marketplace'
@@ -12,8 +13,10 @@ export const marketplaceRouterContract = {
export type MarketPlaceInputs = InferContractRouterInputs<typeof marketplaceRouterContract>
// extend: CVE-2025-63387未授权访问 虽然这个api实际上就是个登录用的 — 路径改为 login_config,需先请求 login_config_bootstrap 写入 cookie
export const consoleRouterContract = {
systemFeatures: systemFeaturesContract,
loginConfigBootstrap: loginConfigBootstrapContract,
loginConfig: loginConfigContract,
trialApps: {
info: trialAppInfoContract,
datasets: trialAppDatasetsContract,
+9
View File
@@ -63,6 +63,15 @@ const nextConfig: NextConfig = {
},
]
},
// dev 时把 /console/api 和 /api 代理到 5001
...(isDev && {
async rewrites() {
return [
{ source: '/console/api/:path*', destination: 'http://localhost:5001/console/api/:path*' },
{ source: '/api/:path*', destination: 'http://localhost:5001/api/:path*' },
]
},
}),
output: 'standalone',
compiler: {
removeConsole: isDev ? false : { exclude: ['warn', 'error'] },
+14
View File
@@ -15,10 +15,23 @@ import {
} from '@/contract/router'
import { request } from './base'
// extend: CVE-2025-63387 跨域时 Cookie 可能为 None,用 Header 携带 JWT
let loginConfigToken: string | null = null
export function setLoginConfigToken(token: string | null) {
loginConfigToken = token
}
const getMarketplaceHeaders = () => new Headers({
'X-Dify-Version': !IS_MARKETPLACE ? APP_VERSION : '999.0.0',
})
const getConsoleHeaders = () => {
const h = new Headers()
if (loginConfigToken)
h.set('X-Login-Config-Token', loginConfigToken)
return h
}
const marketplaceLink = new OpenAPILink(marketplaceRouterContract, {
url: MARKETPLACE_API_PREFIX,
headers: () => (getMarketplaceHeaders()),
@@ -40,6 +53,7 @@ export const marketplaceQuery = createTanstackQueryUtils(marketplaceClient, { pa
const consoleLink = new OpenAPILink(consoleRouterContract, {
url: API_PREFIX,
headers: () => getConsoleHeaders(),
fetch: (input, init) => {
return request(
input.url,
+2 -2
View File
@@ -308,9 +308,9 @@ export const fetchSupportRetrievalMethods = (url: string): Promise<RetrievalMeth
return get<RetrievalMethodsRes>(url)
}
// extend: CVE-2025-63387未授权访问 虽然这个api实际上就是个登录用的 — 路径改为 login_config,需先请求 login_config_bootstrap 写入 cookie
export const getSystemFeatures = (): Promise<SystemFeatures> => {
// extend: 解决登录状况不刷新
return get<SystemFeatures>(`/system-features?time=${(Math.round(new Date() / 1000)).toString()}`)
return get<SystemFeatures>(`/login_config?time=${(Math.round(new Date() / 1000)).toString()}`)
}
export const enableModel = (url: string, body: { model: string, model_type: ModelTypeEnum }): Promise<CommonResponse> =>