feat: 收口 tree-first graph 主链与前端测试修复
This commit is contained in:
@@ -243,6 +243,61 @@ describe("documents bridge helpers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("buildDocumentBridgeMutationRequest builds documents.embed runtime request", () => {
|
||||
const payload = {
|
||||
...buildDocumentSavePayload({
|
||||
documentId: "doc_2",
|
||||
workspaceId: "ws_1",
|
||||
revision: 3,
|
||||
content: [{ id: "block_2", type: "pageReference" }],
|
||||
conflictDetectionKey: "conflict_3",
|
||||
blockCount: 1,
|
||||
}),
|
||||
sourceDocumentId: "doc_1",
|
||||
targetDocumentId: "doc_2",
|
||||
anchorBlockId: "anchor_1",
|
||||
};
|
||||
const envelope = buildDocumentCommandEnvelope({
|
||||
name: "documents.embed",
|
||||
payload,
|
||||
context: mockContext,
|
||||
target: { workspaceId: "ws_1", pageId: "doc_2" },
|
||||
});
|
||||
|
||||
const request = buildDocumentBridgeMutationRequest({
|
||||
context: mockContext,
|
||||
envelope,
|
||||
mapConvexArgs: (nextPayload) => ({
|
||||
id: nextPayload.documentId,
|
||||
content: nextPayload.content,
|
||||
expectedRevision: nextPayload.revision,
|
||||
conflictDetectionKey: nextPayload.conflictDetectionKey,
|
||||
sourceDocumentId: nextPayload.sourceDocumentId,
|
||||
targetDocumentId: nextPayload.targetDocumentId,
|
||||
anchorBlockId: nextPayload.anchorBlockId,
|
||||
}),
|
||||
});
|
||||
|
||||
expect(request.functionName).toBe("documents:updateContent");
|
||||
expect(request.workspaceId).toBe("ws_1");
|
||||
expect(request.args).toEqual({
|
||||
id: "doc_2",
|
||||
content: [{ id: "block_2", type: "pageReference" }],
|
||||
expectedRevision: 3,
|
||||
conflictDetectionKey: "conflict_3",
|
||||
sourceDocumentId: "doc_1",
|
||||
targetDocumentId: "doc_2",
|
||||
anchorBlockId: "anchor_1",
|
||||
});
|
||||
expect(JSON.parse(request.payloadJson)).toMatchObject({
|
||||
kind: "command",
|
||||
name: "documents.embed",
|
||||
workspace_id: "ws_1",
|
||||
request_id: "req_1",
|
||||
trace_id: "trace_1",
|
||||
});
|
||||
});
|
||||
|
||||
it("buildDocumentBridgeMutationRequest builds media asset writeback runtime request", () => {
|
||||
const envelope = buildDocumentCommandEnvelope({
|
||||
name: "media.assets.replace_storage",
|
||||
|
||||
@@ -126,6 +126,7 @@ const DOCUMENT_BRIDGE_MUTATION_FUNCTIONS = {
|
||||
"documents.template": "documents:setTemplate",
|
||||
"documents.emptyTrashByWorkspace": "documents:emptyTrashByWorkspace",
|
||||
"documents.purge": "documents:purge",
|
||||
"documents.embed": "documents:updateContent",
|
||||
"blocks.patch": "documents:updateContent",
|
||||
"blocks.move": "documents:updateContent",
|
||||
"blocks.embed": "documents:updateContent",
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
executeRustBridgeMutationTransport,
|
||||
resolveRustBridgeCommandPlan,
|
||||
} from "@/lib/documents/rust-runtime";
|
||||
import { executeSaveBridgeCommand } from "@/lib/documents/save-command-adapter";
|
||||
import { buildDocumentSavePayload } from "@/lib/documents/save-contract";
|
||||
import type { Json } from "@/types/supabase";
|
||||
|
||||
@@ -79,6 +78,19 @@ export type DocumentPurgePayload = {
|
||||
documentId: string;
|
||||
};
|
||||
|
||||
export type DocumentEmbedPayload = {
|
||||
documentId: string;
|
||||
workspaceId: string | null;
|
||||
revision: number | null;
|
||||
content: Json;
|
||||
conflictDetectionKey: string | null;
|
||||
snapshotCapturedAt: string | null;
|
||||
blockCount: number | null;
|
||||
sourceDocumentId: string;
|
||||
targetDocumentId: string;
|
||||
anchorBlockId: string | null;
|
||||
};
|
||||
|
||||
export type PageCommandExecutionResult<TResult> = {
|
||||
requestId: string;
|
||||
traceId: string;
|
||||
@@ -287,23 +299,28 @@ export async function executeDocumentEmbedBridgeCommand(request: Request): Promi
|
||||
const payload: Json = composeContentWithBlocks(targetContent.content, nextBlocks);
|
||||
const workspaceId = normalizeWorkspaceId(sourceDoc.workspace_id) ?? normalizeWorkspaceId((targetMeta as { workspace_id?: string | null } | null)?.workspace_id);
|
||||
const context = await buildRuntimeContext(request, workspaceId);
|
||||
const savePayload = buildDocumentSavePayload({
|
||||
documentId: normalizedTargetId,
|
||||
workspaceId,
|
||||
revision:
|
||||
typeof targetContent.revision === "number" && Number.isInteger(targetContent.revision)
|
||||
? targetContent.revision
|
||||
: null,
|
||||
content: payload,
|
||||
conflictDetectionKey:
|
||||
typeof targetContent.conflict_detection_key === "string"
|
||||
? targetContent.conflict_detection_key
|
||||
: null,
|
||||
blockCount: nextBlocks.length,
|
||||
});
|
||||
const embedPayload: DocumentEmbedPayload = {
|
||||
...buildDocumentSavePayload({
|
||||
documentId: normalizedTargetId,
|
||||
workspaceId,
|
||||
revision:
|
||||
typeof targetContent.revision === "number" && Number.isInteger(targetContent.revision)
|
||||
? targetContent.revision
|
||||
: null,
|
||||
content: payload,
|
||||
conflictDetectionKey:
|
||||
typeof targetContent.conflict_detection_key === "string"
|
||||
? targetContent.conflict_detection_key
|
||||
: null,
|
||||
blockCount: nextBlocks.length,
|
||||
}),
|
||||
sourceDocumentId: normalizedSourceId,
|
||||
targetDocumentId: normalizedTargetId,
|
||||
anchorBlockId: anchorId,
|
||||
};
|
||||
const envelope = buildDocumentCommandEnvelope({
|
||||
name: "documents.save",
|
||||
payload: savePayload,
|
||||
name: "documents.embed",
|
||||
payload: embedPayload,
|
||||
context,
|
||||
target: {
|
||||
workspaceId,
|
||||
@@ -311,9 +328,13 @@ export async function executeDocumentEmbedBridgeCommand(request: Request): Promi
|
||||
},
|
||||
});
|
||||
|
||||
const result = await executeSaveBridgeCommand({
|
||||
const result = await executePageLifecycleBridgeCommand<DocumentEmbedPayload, {
|
||||
revision?: number | null;
|
||||
conflict_detection_key?: string | null;
|
||||
}>({
|
||||
context,
|
||||
envelope,
|
||||
client,
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { buildParentById } from "@/lib/file-tree/dnd";
|
||||
|
||||
vi.mock("next/server", () => ({
|
||||
NextResponse: {
|
||||
json: (body: unknown, init?: { status?: number }) => ({
|
||||
body,
|
||||
status: init?.status ?? 200,
|
||||
}),
|
||||
},
|
||||
}), { virtual: true });
|
||||
|
||||
vi.mock("@/lib/convex/api", () => ({
|
||||
api: {
|
||||
documents: { getMeta: "documents:getMeta" },
|
||||
workspaces: { ensureDefaultWorkspace: "workspaces:ensureDefaultWorkspace" },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/convex/route", () => ({
|
||||
getAuthedConvexClient: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/documents/bridge", () => ({
|
||||
buildDocumentBridgeContextWithActor: vi.fn(),
|
||||
buildDocumentCommandEnvelope: vi.fn(),
|
||||
documentBridgeErrorResponse: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/documents/bridge-log", () => ({
|
||||
recordBridgeCommandArtifacts: vi.fn(),
|
||||
recordBridgeCommandFailureArtifacts: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/documents/rust-runtime", () => ({
|
||||
executeRustBridgeMutationTransport: vi.fn(),
|
||||
resolveRustBridgeCommandPlan: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/server/local-paths", () => ({
|
||||
getDocumentsBaseDir: () => "/tmp/mnote-vitest-documents",
|
||||
}));
|
||||
|
||||
const {
|
||||
normalizeDocumentCopyTreePayload,
|
||||
normalizeDocumentMovePayload,
|
||||
resolveSubtreeMoveLegality,
|
||||
} = await import("./page-lifecycle-command-adapter");
|
||||
|
||||
describe("page-lifecycle-command-adapter", () => {
|
||||
it("归一化 move payload 的 targetParentId 与 sortOrder", () => {
|
||||
expect(
|
||||
normalizeDocumentMovePayload({
|
||||
documentId: " doc_1 ",
|
||||
parentId: " parent_1 ",
|
||||
position: 2.8,
|
||||
}),
|
||||
).toEqual({
|
||||
documentId: "doc_1",
|
||||
parentId: "parent_1",
|
||||
sortOrder: 2,
|
||||
});
|
||||
|
||||
expect(
|
||||
normalizeDocumentMovePayload({
|
||||
documentId: "doc_1",
|
||||
parentId: " ",
|
||||
position: Number.NaN,
|
||||
}),
|
||||
).toEqual({
|
||||
documentId: "doc_1",
|
||||
parentId: null,
|
||||
sortOrder: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("归一化 copy_tree payload 并去重 source ids", () => {
|
||||
expect(
|
||||
normalizeDocumentCopyTreePayload({
|
||||
targetParentId: " parent_1 ",
|
||||
items: [
|
||||
{ documentId: " doc_a ", recursive: true },
|
||||
{ documentId: "doc_b", recursive: false },
|
||||
{ documentId: "doc_a", recursive: false },
|
||||
{ documentId: " ", recursive: true },
|
||||
],
|
||||
}),
|
||||
).toEqual({
|
||||
targetParentId: "parent_1",
|
||||
items: [
|
||||
{ documentId: "doc_a", recursive: true },
|
||||
{ documentId: "doc_b", recursive: false },
|
||||
{ documentId: "doc_a", recursive: false },
|
||||
],
|
||||
sourceIds: ["doc_a", "doc_b"],
|
||||
});
|
||||
});
|
||||
|
||||
it("在 subtree move legality 中先收敛顶层 source,再拦截自拖拽/拖入后代", () => {
|
||||
const parentById = buildParentById([
|
||||
{ id: "a", parentId: null },
|
||||
{ id: "b", parentId: "a" },
|
||||
{ id: "c", parentId: "b" },
|
||||
{ id: "d", parentId: null },
|
||||
]);
|
||||
|
||||
expect(
|
||||
resolveSubtreeMoveLegality({
|
||||
sourceDocIds: ["a", "b", "c", "d"],
|
||||
targetParentId: "b",
|
||||
parentById,
|
||||
}),
|
||||
).toEqual({
|
||||
sourceDocIds: ["a", "d"],
|
||||
targetParentId: "b",
|
||||
isInvalid: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
resolveSubtreeMoveLegality({
|
||||
sourceDocIds: ["b", "c"],
|
||||
targetParentId: "a",
|
||||
parentById,
|
||||
}),
|
||||
).toEqual({
|
||||
sourceDocIds: ["b"],
|
||||
targetParentId: "a",
|
||||
isInvalid: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
import "server-only";
|
||||
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
@@ -19,6 +17,7 @@ import {
|
||||
} from "@/lib/documents/bridge-log";
|
||||
import { executeRustBridgeMutationTransport, resolveRustBridgeCommandPlan } from "@/lib/documents/rust-runtime";
|
||||
import { getDocumentsBaseDir } from "@/lib/server/local-paths";
|
||||
import { filterTopLevelDocIds, isInvalidDocDrop } from "@/lib/file-tree/dnd";
|
||||
|
||||
type CreatePayload = {
|
||||
parentId?: string | null;
|
||||
@@ -52,8 +51,34 @@ type CopyTreePayload = {
|
||||
targetParentId?: string | null;
|
||||
};
|
||||
|
||||
export type NormalizedDocumentMovePayload = {
|
||||
documentId: string | null;
|
||||
parentId: string | null;
|
||||
sortOrder: number;
|
||||
};
|
||||
|
||||
export type NormalizedCopyTreeItem = {
|
||||
documentId: string;
|
||||
recursive: boolean;
|
||||
};
|
||||
|
||||
export type NormalizedDocumentCopyTreePayload = {
|
||||
targetParentId: string | null;
|
||||
items: NormalizedCopyTreeItem[];
|
||||
sourceIds: string[];
|
||||
};
|
||||
|
||||
const documentsBaseDir = getDocumentsBaseDir();
|
||||
|
||||
function assertServerEnvironment() {
|
||||
if (typeof process !== "undefined" && process.env.VITEST === "true") {
|
||||
return;
|
||||
}
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("page-lifecycle-command-adapter 仅允许在服务端执行");
|
||||
}
|
||||
}
|
||||
|
||||
function trimOrNull(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const trimmed = value.trim();
|
||||
@@ -65,6 +90,52 @@ function normalizeTitle(title: string | null): string {
|
||||
return safe && safe.length > 0 ? safe : "无标题";
|
||||
}
|
||||
|
||||
export function normalizeDocumentMovePayload(payload: MovePayload): NormalizedDocumentMovePayload {
|
||||
return {
|
||||
documentId: trimOrNull(payload.documentId),
|
||||
parentId: trimOrNull(payload.parentId),
|
||||
sortOrder: Number.isFinite(payload.position ?? Number.NaN) ? Math.floor(payload.position ?? 0) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeDocumentCopyTreePayload(
|
||||
payload: CopyTreePayload,
|
||||
): NormalizedDocumentCopyTreePayload {
|
||||
const items = (payload.items ?? [])
|
||||
.map((item) => ({
|
||||
documentId: trimOrNull(item?.documentId),
|
||||
recursive: Boolean(item?.recursive),
|
||||
}))
|
||||
.filter((item): item is NormalizedCopyTreeItem => Boolean(item.documentId))
|
||||
.map((item) => ({
|
||||
documentId: item.documentId,
|
||||
recursive: item.recursive,
|
||||
}));
|
||||
|
||||
return {
|
||||
targetParentId: trimOrNull(payload.targetParentId),
|
||||
items,
|
||||
sourceIds: Array.from(new Set(items.map((item) => item.documentId))),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveSubtreeMoveLegality(input: {
|
||||
sourceDocIds: string[];
|
||||
targetParentId: string | null;
|
||||
parentById: Map<string, string | null>;
|
||||
}) {
|
||||
const topLevelSourceDocIds = filterTopLevelDocIds(input.sourceDocIds, input.parentById);
|
||||
return {
|
||||
sourceDocIds: topLevelSourceDocIds,
|
||||
targetParentId: input.targetParentId,
|
||||
isInvalid: isInvalidDocDrop({
|
||||
sourceDocIds: topLevelSourceDocIds,
|
||||
targetParentId: input.targetParentId,
|
||||
parentById: input.parentById,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function safeRandomId() {
|
||||
return typeof crypto.randomUUID === "function"
|
||||
? crypto.randomUUID()
|
||||
@@ -138,6 +209,7 @@ async function handleLifecycleError(error: unknown) {
|
||||
}
|
||||
|
||||
export async function handleDocumentCreateRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as CreatePayload;
|
||||
const { auth, client } = await getAuthedConvexClient();
|
||||
@@ -185,7 +257,7 @@ export async function handleDocumentCreateRequest(request: Request): Promise<Nex
|
||||
pageId: documentId,
|
||||
},
|
||||
});
|
||||
const { context, plan } = await resolveCommandPlan({
|
||||
const { context: runtimeContext, plan } = await resolveCommandPlan({
|
||||
request,
|
||||
workspaceId,
|
||||
authUserId: auth.userId,
|
||||
@@ -213,7 +285,7 @@ export async function handleDocumentCreateRequest(request: Request): Promise<Nex
|
||||
}
|
||||
|
||||
await recordBridgeCommandArtifacts({
|
||||
context,
|
||||
context: runtimeContext,
|
||||
envelope,
|
||||
client,
|
||||
});
|
||||
@@ -255,9 +327,11 @@ export async function handleDocumentCreateRequest(request: Request): Promise<Nex
|
||||
}
|
||||
|
||||
export async function handleDocumentMoveRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as MovePayload;
|
||||
const documentId = trimOrNull(payload.documentId);
|
||||
const normalizedMove = normalizeDocumentMovePayload(payload);
|
||||
const documentId = normalizedMove.documentId;
|
||||
if (!documentId) {
|
||||
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
|
||||
}
|
||||
@@ -273,8 +347,8 @@ export async function handleDocumentMoveRequest(request: Request): Promise<NextR
|
||||
name: "documents.move",
|
||||
payload: {
|
||||
documentId,
|
||||
parentId: trimOrNull(payload.parentId),
|
||||
sortOrder: Number.isFinite(payload.position ?? Number.NaN) ? Math.floor(payload.position ?? 0) : 0,
|
||||
parentId: normalizedMove.parentId,
|
||||
sortOrder: normalizedMove.sortOrder,
|
||||
},
|
||||
context,
|
||||
target: {
|
||||
@@ -329,6 +403,7 @@ export async function handleDocumentMoveRequest(request: Request): Promise<NextR
|
||||
}
|
||||
|
||||
export async function handleDocumentDeleteRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as DeletePayload;
|
||||
const documentId = trimOrNull(payload.documentId);
|
||||
@@ -399,6 +474,7 @@ export async function handleDocumentDeleteRequest(request: Request): Promise<Nex
|
||||
}
|
||||
|
||||
export async function handleDocumentRestoreRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as RestorePayload;
|
||||
const documentId = trimOrNull(payload.documentId);
|
||||
@@ -473,6 +549,7 @@ export async function handleDocumentRestoreRequest(request: Request): Promise<Ne
|
||||
}
|
||||
|
||||
export async function handleDocumentDuplicateRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as DuplicatePayload;
|
||||
const documentId = trimOrNull(payload.documentId);
|
||||
@@ -569,16 +646,15 @@ export async function handleDocumentDuplicateRequest(request: Request): Promise<
|
||||
}
|
||||
|
||||
export async function handleDocumentCopyTreeRequest(request: Request): Promise<NextResponse> {
|
||||
assertServerEnvironment();
|
||||
try {
|
||||
const payload = (await request.json()) as CopyTreePayload;
|
||||
const { auth, client } = await getAuthedConvexClient();
|
||||
|
||||
const normalizedItems = (payload.items ?? []).filter((it) => trimOrNull(it?.documentId));
|
||||
if (normalizedItems.length === 0) {
|
||||
const normalizedCopyTree = normalizeDocumentCopyTreePayload(payload);
|
||||
if (normalizedCopyTree.items.length === 0) {
|
||||
return NextResponse.json({ error: "items 为空" }, { status: 400 });
|
||||
}
|
||||
|
||||
const targetParentId = trimOrNull(payload.targetParentId);
|
||||
const targetParentId = normalizedCopyTree.targetParentId;
|
||||
let workspaceId: string | null = null;
|
||||
|
||||
if (targetParentId) {
|
||||
@@ -589,7 +665,7 @@ export async function handleDocumentCopyTreeRequest(request: Request): Promise<N
|
||||
workspaceId = targetDoc.workspace_id;
|
||||
}
|
||||
|
||||
const sourceIds = Array.from(new Set(normalizedItems.map((it) => trimOrNull(it.documentId) as string)));
|
||||
const sourceIds = normalizedCopyTree.sourceIds;
|
||||
const firstMeta = await client.query(api.documents.getMeta, { id: sourceIds[0] });
|
||||
if (!firstMeta) {
|
||||
return NextResponse.json({ error: "源页面不存在或无权限" }, { status: 404 });
|
||||
@@ -607,10 +683,7 @@ export async function handleDocumentCopyTreeRequest(request: Request): Promise<N
|
||||
const outerEnvelope = buildDocumentCommandEnvelope({
|
||||
name: "documents.copy_tree",
|
||||
payload: {
|
||||
items: normalizedItems.map((item) => ({
|
||||
documentId: trimOrNull(item.documentId) as string,
|
||||
recursive: Boolean(item.recursive),
|
||||
})),
|
||||
items: normalizedCopyTree.items,
|
||||
targetParentId,
|
||||
},
|
||||
context,
|
||||
|
||||
@@ -89,6 +89,7 @@ export type PageSubtreeProjection = {
|
||||
};
|
||||
|
||||
const SNIPPET_MAX_LENGTH = 220;
|
||||
const PAGE_SUBTREE_PROJECTION_ID = "kernel_projection:page_tree:document_subtree";
|
||||
|
||||
const pickFirstText = (...values: unknown[]) => {
|
||||
for (const value of values) {
|
||||
@@ -367,7 +368,7 @@ export function buildPageSubtreeProjection(input: {
|
||||
}
|
||||
|
||||
return {
|
||||
projectionId: `page_subtree:${rootNodeId}`,
|
||||
projectionId: PAGE_SUBTREE_PROJECTION_ID,
|
||||
projection: "page_tree",
|
||||
rootNodeId,
|
||||
rootNode,
|
||||
|
||||
@@ -43,6 +43,33 @@ type MoveDocumentInput = {
|
||||
workspaceId?: string | null;
|
||||
};
|
||||
|
||||
type DeleteDocumentInput = {
|
||||
documentId: string;
|
||||
workspaceId?: string | null;
|
||||
};
|
||||
|
||||
type RestoreDocumentInput = {
|
||||
documentId: string;
|
||||
workspaceId?: string | null;
|
||||
};
|
||||
|
||||
type PurgeDocumentInput = {
|
||||
documentId: string;
|
||||
};
|
||||
|
||||
type EmbedDocumentInput = {
|
||||
sourceId: string;
|
||||
targetId: string;
|
||||
};
|
||||
|
||||
type CopyTreeCommandInput = {
|
||||
targetParentId: string | null;
|
||||
items: Array<{
|
||||
documentId: string;
|
||||
recursive: boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
type CreateChildDocumentInput = {
|
||||
parentId: string | null;
|
||||
title: string;
|
||||
@@ -106,3 +133,73 @@ export async function moveDocumentCommand(input: MoveDocumentInput): Promise<{ o
|
||||
"移动失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
export async function deleteDocumentCommand(
|
||||
input: DeleteDocumentInput,
|
||||
): Promise<{ success: true; meta?: DocumentCommandMeta }> {
|
||||
return postDocumentCommand<{ success: true; meta?: DocumentCommandMeta }>(
|
||||
"/api/documents/delete",
|
||||
{
|
||||
documentId: input.documentId,
|
||||
workspaceId: input.workspaceId ?? null,
|
||||
},
|
||||
"删除失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
export async function restoreDocumentCommand(
|
||||
input: RestoreDocumentInput,
|
||||
): Promise<{ success: true; meta?: DocumentCommandMeta }> {
|
||||
return postDocumentCommand<{ success: true; meta?: DocumentCommandMeta }>(
|
||||
"/api/documents/restore",
|
||||
{
|
||||
documentId: input.documentId,
|
||||
workspaceId: input.workspaceId ?? null,
|
||||
},
|
||||
"恢复失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
export async function purgeDocumentCommand(
|
||||
input: PurgeDocumentInput,
|
||||
): Promise<{ success: true; purged?: boolean; meta?: DocumentCommandMeta }> {
|
||||
return postDocumentCommand<{ success: true; purged?: boolean; meta?: DocumentCommandMeta }>(
|
||||
"/api/documents/purge",
|
||||
{
|
||||
documentId: input.documentId,
|
||||
},
|
||||
"彻底删除失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
export async function embedDocumentCommand(
|
||||
input: EmbedDocumentInput,
|
||||
): Promise<{ ok: true; meta?: DocumentCommandMeta }> {
|
||||
return postDocumentCommand<{ ok: true; meta?: DocumentCommandMeta }>(
|
||||
"/api/documents/embed",
|
||||
{
|
||||
sourceId: input.sourceId,
|
||||
targetId: input.targetId,
|
||||
},
|
||||
"嵌入失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
export async function copyTreeCommand(
|
||||
input: CopyTreeCommandInput,
|
||||
): Promise<{
|
||||
items: Array<{ oldId: string; newId: string }>;
|
||||
meta?: DocumentCommandMeta;
|
||||
}> {
|
||||
return postDocumentCommand<{
|
||||
items: Array<{ oldId: string; newId: string }>;
|
||||
meta?: DocumentCommandMeta;
|
||||
}>(
|
||||
"/api/documents/copy-tree",
|
||||
{
|
||||
targetParentId: input.targetParentId,
|
||||
items: input.items,
|
||||
},
|
||||
"复制页面失败,请稍后再试",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user