Files
deerflow-code/frontend-web/src/core/studio/api.ts
T
xuhdandClaude Sonnet 4.6 613cece224 fix(studio): 笔记切换页面不刷新 + 侧边栏错误提示友好化
- WorkspaceRoutes 给 StudioNotebookDetailPage 加 key={notebookId},切换笔记时强制重新挂载
- createSearchSpace 接口失败时解析响应体 detail 字段,直接展示服务端错误信息

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 17:24:08 +08:00

157 lines
4.9 KiB
TypeScript

import { getNotebookApiBaseURL } from "@/core/config";
import type { SourceItem, StudioNotebook } from "./types";
function base(): string {
return getNotebookApiBaseURL();
}
export async function fetchStudioToken(email: string): Promise<string> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(
`${apiBase}/api/getTokenByEmail?email=${encodeURIComponent(email)}`,
);
if (!res.ok) throw new Error(`获取 Token 失败:${res.status}`);
const data = (await res.json()) as { token?: string } | string;
const token = typeof data === "string" ? data : data?.token;
if (!token) throw new Error("接口未返回有效 token");
return token;
}
export async function fetchStudioNotebooks(token: string): Promise<StudioNotebook[]> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/searchspaces`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(`获取笔记本列表失败:${res.status}`);
return res.json() as Promise<StudioNotebook[]>;
}
export async function createSearchSpace(
name: string,
token: string,
): Promise<void> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/searchspaces`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ name, description: "", citations_enabled: true }),
});
if (!res.ok) {
const body = await res.json().catch(() => null);
throw new Error(body?.detail || `创建失败:${res.status}`);
}
}
export async function uploadSourceText(
notebookId: number,
content: string,
title: string,
token: string,
): Promise<void> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/documents/textupload`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify({ search_space_id: notebookId, content, title: title || "粘贴的文字" }),
});
if (!res.ok) throw new Error(`上传失败:${res.status}`);
}
export async function deleteSearchSpace(id: number, token: string): Promise<void> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/searchspaces/${id}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(`删除失败:${res.status}`);
}
export async function renameSearchSpace(id: number, name: string, token: string): Promise<void> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/searchspaces/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify({ name }),
});
if (!res.ok) throw new Error(`重命名失败:${res.status}`);
}
export interface ChunkRead {
id: number;
content: string;
document_id: number;
created_at: string;
}
export interface DocumentWithChunks {
id: number;
title: string;
document_type: string;
file_type: string;
chunks: ChunkRead[];
}
export async function fetchDocumentByChunk(
chunkId: number,
token: string,
): Promise<DocumentWithChunks> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(`${apiBase}/api/v1/documents/by-chunk/${chunkId}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) throw new Error(`获取文档失败:${res.status}`);
return res.json() as Promise<DocumentWithChunks>;
}
export async function uploadSourceFiles(
notebookId: number,
files: File[],
token: string,
): Promise<void> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const formData = new FormData();
formData.append("search_space_id", String(notebookId));
for (const file of files) {
formData.append("files", file);
}
const res = await fetch(`${apiBase}/api/v1/documents/fileupload`, {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: formData,
});
if (!res.ok) throw new Error(`上传失败:${res.status}`);
}
export async function fetchNotebookSources(
notebookId: number,
token: string,
): Promise<SourceItem[]> {
const apiBase = base();
if (!apiBase) throw new Error("VITE_AIPI_FRONTEND_URL 未配置");
const res = await fetch(
`${apiBase}/api/v1/source/search-space/${notebookId}?limit=100`,
{ headers: { Authorization: `Bearer ${token}` } },
);
if (!res.ok) throw new Error(`获取来源失败:${res.status}`);
return res.json() as Promise<SourceItem[]>;
}