Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Admin API;Chat 目录作为独立体系预留。

```text
src/api/
├── constants.ts # Admin 与 Chat 的 API base 路径常量
├── admin/
│ ├── auth/ # Admin 登录认证与当前用户接口
│ │ └── types.ts # 认证 API 与认证 Store 共用类型
Expand Down Expand Up @@ -154,8 +155,12 @@ API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文

## 请求约定

- Admin 业务接口只声明相对资源路径,`/admin/api` 等部署前缀由 `core/request.ts` 统一处理。
- Admin Router 与请求客户端直接读取 `window.MaxKB` 运行时路径配置;`Window` 和
- `constants.ts` 统一导出 `ADMIN_API_BASE_PATH` 和 `CHAT_API_BASE_PATH`,分别优先读取
`window.MaxKB.prefix` 和 `window.MaxKB.chatPrefix`,再回退到 `VITE_BASE_PATH` 和各自默认路径;
去掉末尾斜杠后追加 `/api`。Admin、Chat 请求客户端和会话流式请求复用这些常量。
- Admin 普通业务接口只声明相对资源路径,由 `core/request.ts` 的 Axios baseURL 处理部署前缀;
流式接口显式传入对应的 API base 常量。
- Admin Router 直接读取 `window.MaxKB` 运行时路径配置,请求客户端通过上述常量读取;`Window` 和
`MaxKBRuntimeConfig` 的全局类型统一声明在根目录 `env.d.ts`。
- Admin 普通 JSON 请求使用 Axios;`request.ts` 导出 Axios 实例以及 `promise`、`get`、
`post`、`put`、`del`、流式响应 `postStream` 和 Blob 文件 `downloadRequest` 请求封装。
Expand All @@ -171,6 +176,7 @@ API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文
登录页。
- loading 不作为 API 函数参数,由调用接口的页面或 Store 管理。
- 流式 POST 请求使用 `postStream` 返回原始 `Response`,由业务组件按具体协议解析数据块;
参数顺序为 `postStream(base, path, data?, config?)`,`config.signal` 用于取消请求。
鉴权、语言请求头和错误状态仍由请求基础设施统一处理。
- 上传、下载和其他特殊请求在真实需求出现时独立设计,不提前塞入普通 JSON 请求客户端。

Expand Down
59 changes: 30 additions & 29 deletions ui/src/api/admin/core/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { useStore } from '@/stores'
import type { ApiResponse, LoadingTarget } from './types'
import type { Dict } from '@/api/types'
import { MsgError } from '@/utils/message'
import { ADMIN_API_BASE_PATH } from '@/api/constants'

const DEFAULT_TIMEOUT = 30 * 60 * 1_000 // 30 minutes
const ADMIN_BASE_PATH = window.MaxKB?.prefix || import.meta.env.VITE_BASE_PATH || '/admin/'

interface ExportRequestConfig extends AxiosRequestConfig {
skipGlobalErrorMessage?: boolean
Expand Down Expand Up @@ -128,7 +128,7 @@ async function downloadExportResponse(response: AxiosResponse<Blob>, fileName: s
return true
}

export const request = axios.create({ baseURL: `${ADMIN_BASE_PATH.replace(/\/+$/, '')}/api`, timeout: DEFAULT_TIMEOUT, withCredentials: false })
export const request = axios.create({ baseURL: ADMIN_API_BASE_PATH, timeout: DEFAULT_TIMEOUT, withCredentials: false })

request.interceptors.request.use(setRequestHeaders)

Expand Down Expand Up @@ -195,29 +195,9 @@ export function get<T = unknown>(url: string, params?: Dict<unknown>, loading?:
return promise<T>(request.get<ApiResponse<T>>(url, { params, timeout }), loading)
}

/** 发送指定方法的 Blob 请求并触发浏览器下载。 */
export async function downloadRequest(
url: string,
method: string,
data?: unknown,
params?: Dict<unknown>,
loading?: LoadingTarget,
): Promise<boolean> {
startLoading(loading)
try {
const response = await request.request<Blob>({
url,
method,
data,
params,
responseType: 'blob',
skipGlobalErrorMessage: true,
} as ExportRequestConfig)

return downloadExportResponse(response, 'download')
} finally {
finishLoading(loading)
}
/** 发送 POST 请求。 */
export function post<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, loading?: LoadingTarget, timeout?: number) {
return promise<T>(request.post<ApiResponse<T>>(url, data, { params, timeout }), loading)
}

/** 发送 GET 请求并将 Blob 响应下载为文件。 */
Expand Down Expand Up @@ -250,13 +230,33 @@ export async function postExportExcel<TData = unknown>(
}
}

/** 发送 POST 请求。 */
export function post<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, loading?: LoadingTarget, timeout?: number) {
return promise<T>(request.post<ApiResponse<T>>(url, data, { params, timeout }), loading)
/** 发送指定方法的 Blob 请求并触发浏览器下载。 */
export async function downloadRequest(
url: string,
method: string,
data?: unknown,
params?: Dict<unknown>,
loading?: LoadingTarget,
): Promise<boolean> {
startLoading(loading)
try {
const response = await request.request<Blob>({
url,
method,
data,
params,
responseType: 'blob',
skipGlobalErrorMessage: true,
} as ExportRequestConfig)

return downloadExportResponse(response, 'download')
} finally {
finishLoading(loading)
}
}

/** 发送 POST 请求并返回可逐块读取的原始响应。 */
export function postStream(base: string, path: string, data?: unknown) {
export function postStream(base: string, path: string, data?: unknown, config?: StreamRequestConfig): Promise<Response> {
const { auth, user } = useStore()
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
if (auth.token) {
Expand All @@ -269,6 +269,7 @@ export function postStream(base: string, path: string, data?: unknown) {
method: 'POST',
headers,
body: data === undefined ? undefined : JSON.stringify(data),
signal: config?.signal,
})
}

Expand Down
3 changes: 2 additions & 1 deletion ui/src/api/admin/workspace/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { del, get, getExportFile, post, postStream, put } from '../../core/reque
import type { ParamsPage, ResponsePage } from '../../core/types'
import type { ApplicationDetail, ApplicationFormPayload, Dict, PromptGeneratePayload } from '@/api/types'
import { getWorkspaceId } from '@/utils/resource-context'
import { ADMIN_API_BASE_PATH } from '@/api/constants'

const getPrefix = () => {
const workspaceId = getWorkspaceId()
Expand Down Expand Up @@ -74,7 +75,7 @@ const putApplicationPublish = (applicationId: string) => {

/** 使用指定模型流式生成或优化系统提示词。 */
const postPromptGenerate = (applicationId: string, modelId: string, payload: PromptGeneratePayload, signal?: AbortSignal) => {
return postStream(`${getPrefix()}/${applicationId}/model/${modelId}/prompt_generate`, payload, { signal })
return postStream(ADMIN_API_BASE_PATH, `${getPrefix()}/${applicationId}/model/${modelId}/prompt_generate`, payload, { signal })
}

export default {
Expand Down
4 changes: 2 additions & 2 deletions ui/src/api/chat/core/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { useStore } from '@/stores'
import type { ApiResponse, LoadingTarget } from './types'
import type { Dict } from '@/api/types'
import { MsgError } from '@/utils/message'
import { CHAT_API_BASE_PATH } from '@/api/constants'

const DEFAULT_TIMEOUT = 30 * 60 * 1_000 // 30 minutes
const CHAT_BASE_PATH = window.MaxKB?.chatPrefix || import.meta.env.VITE_BASE_PATH || '/chat/'

function setRequestHeaders(config: InternalAxiosRequestConfig) {
const { auth, user } = useStore()
Expand Down Expand Up @@ -64,7 +64,7 @@ async function getResponseErrorMessage(error: unknown) {
}

export const request = axios.create({
baseURL: `${CHAT_BASE_PATH.replace(/\/+$/, '')}/api`,
baseURL: CHAT_API_BASE_PATH,
timeout: DEFAULT_TIMEOUT,
withCredentials: false,
})
Expand Down
6 changes: 6 additions & 0 deletions ui/src/api/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** Admin 与 Chat 的 API 部署路径配置。 */
const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '')

export const ADMIN_API_BASE_PATH = trimTrailingSlash(window.MaxKB?.prefix || import.meta.env.VITE_BASE_PATH || '/admin/') + '/api'

export const CHAT_API_BASE_PATH = trimTrailingSlash(window.MaxKB?.chatPrefix || import.meta.env.VITE_BASE_PATH || '/chat/') + '/api'
8 changes: 1 addition & 7 deletions ui/src/components/conversation/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,7 @@ import {
} from '@/api/chat/core/request'
import { useStore } from '@/stores'
import { getWorkspaceId } from '@/utils/resource-context'

const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '')
const adminApiBase =
trimTrailingSlash(window.MaxKB?.prefix || import.meta.env.VITE_BASE_PATH || '/admin/') + '/api'

const chatApiBase =
trimTrailingSlash(window.MaxKB?.chatPrefix || import.meta.env.VITE_BASE_PATH || '/chat/') + '/api'
import { ADMIN_API_BASE_PATH as adminApiBase, CHAT_API_BASE_PATH as chatApiBase } from '@/api/constants'

/** 发送流式 POST 请求,返回原始 `Response` 供 SSE 读取。 */
function postStream(base: string, path: string, data?: unknown) {
Expand Down
Loading