feat: 完成 rust cutover phase 8 收口

This commit is contained in:
lix-2026
2026-04-15 20:01:12 +08:00
parent 822c730cd6
commit 98db79b301
104 changed files with 18777 additions and 3025 deletions
@@ -1,9 +1,9 @@
import { randomUUID } from "crypto";
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, findBlockInTree, withBlocksWrittenBack } from "@/lib/blocks";
import {
documentBridgeErrorResponse,
executeBlockEmbedCommand,
} from "@/lib/blocks/block-command-adapter";
type EmbedBlockPayload = {
sourceDocumentId: string;
@@ -24,42 +24,26 @@ export async function POST(request: Request) {
}
if (isConvexEnabled()) {
const { client } = await getAuthedConvexClient();
const source = await client.query(api.documents.getContent, { id: sourceDocumentId });
if (!source) return NextResponse.json({ error: "源页面不存在或无权限" }, { status: 404 });
const sourceBlocks = getBlocksFromDocumentContent(source.content);
const hit = findBlockInTree(sourceBlocks, blockId);
if (!hit) return NextResponse.json({ error: "源块不存在或无权限" }, { status: 404 });
const target = await client.query(api.documents.getContent, { id: targetDocumentId });
if (!target) return NextResponse.json({ error: "目标页面不存在或无权限" }, { status: 404 });
const targetMeta = await client.query(api.documents.getMeta, { id: targetDocumentId });
const targetBlocks = getBlocksFromDocumentContent(target.content);
const anchorId = (targetMeta as any)?.embed_default_block_id as string | null | undefined;
const anchorIndex =
typeof anchorId === "string" && anchorId.trim()
? targetBlocks.findIndex((b) => String((b as any)?.id ?? "") === anchorId)
: -1;
const insertIndex = anchorIndex >= 0 ? anchorIndex + 1 : targetBlocks.length;
const referenceBlock = {
id: randomUUID(),
type: "blockReference",
props: {
try {
const result = await executeBlockEmbedCommand({
request,
sourceDocumentId,
targetBlockId: blockId,
display: "embed",
},
content: [],
children: [],
};
const nextBlocks = [...targetBlocks.slice(0, insertIndex), referenceBlock, ...targetBlocks.slice(insertIndex)];
const payload = withBlocksWrittenBack(target.content, nextBlocks);
await client.mutation(api.documents.updateContent, { id: targetDocumentId, content: payload });
return NextResponse.json({ ok: true });
blockId,
targetDocumentId,
});
return NextResponse.json({
ok: true,
...(result.result && typeof result.result === "object" ? result.result : {}),
meta: {
requestId: result.requestId,
traceId: result.traceId,
commandId: result.commandId,
commandName: result.commandName,
},
});
} catch (error) {
return documentBridgeErrorResponse(error);
}
}
return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 });
@@ -121,3 +105,5 @@ export async function POST(request: Request) {
return NextResponse.json({ ok: true });
*/
}
export const runtime = "nodejs";
+16 -10
View File
@@ -1,8 +1,9 @@
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, findBlockInTree } from "@/lib/blocks";
import {
documentBridgeErrorResponse,
executeBlockGetQuery,
} from "@/lib/blocks/block-command-adapter";
export async function GET(request: Request) {
const url = new URL(request.url);
@@ -14,13 +15,16 @@ export async function GET(request: Request) {
}
if (isConvexEnabled()) {
const { client } = await getAuthedConvexClient();
const doc = await client.query(api.documents.getContent, { id: sourceDocumentId });
if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 });
const blocks = getBlocksFromDocumentContent(doc.content);
const hit = findBlockInTree(blocks, blockId);
if (!hit) return NextResponse.json({ error: "块不存在或无权限" }, { status: 404 });
return NextResponse.json({ ok: true, block: hit.block });
try {
const result = await executeBlockGetQuery({
request,
sourceDocumentId,
blockId,
});
return NextResponse.json({ ok: true, block: result.block, meta: result.meta });
} catch (error) {
return documentBridgeErrorResponse(error);
}
}
return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 });
@@ -47,3 +51,5 @@ export async function GET(request: Request) {
return NextResponse.json({ ok: true, block: hit.block });
*/
}
export const runtime = "nodejs";
+30 -22
View File
@@ -1,8 +1,9 @@
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, removeBlockSubtree, withBlocksWrittenBack } from "@/lib/blocks";
import {
documentBridgeErrorResponse,
executeBlockMoveCommand,
} from "@/lib/blocks/block-command-adapter";
type MoveBlockPayload = {
sourceDocumentId: string;
@@ -24,25 +25,30 @@ export async function POST(request: Request) {
}
if (isConvexEnabled()) {
const { client } = await getAuthedConvexClient();
const source = await client.query(api.documents.getContent, { id: sourceDocumentId });
if (!source) return NextResponse.json({ error: "源页面不存在或无权限" }, { status: 404 });
const sourceBlocks = getBlocksFromDocumentContent(source.content);
const removedRes = removeBlockSubtree(sourceBlocks, blockId);
if (!removedRes.removed) return NextResponse.json({ error: "源块不存在或无权限" }, { status: 404 });
const target = await client.query(api.documents.getContent, { id: targetDocumentId });
if (!target) return NextResponse.json({ error: "目标页面不存在或无权限" }, { status: 404 });
const targetBlocks = getBlocksFromDocumentContent(target.content);
const nextSourcePayload = withBlocksWrittenBack(source.content, removedRes.nextBlocks);
const nextTargetPayload = withBlocksWrittenBack(target.content, [...targetBlocks, removedRes.removed]);
await client.mutation(api.documents.updateContent, { id: sourceDocumentId, content: nextSourcePayload });
await client.mutation(api.documents.updateContent, { id: targetDocumentId, content: nextTargetPayload });
return NextResponse.json({ ok: true });
try {
const result = await executeBlockMoveCommand({
request,
sourceDocumentId,
blockId,
targetDocumentId,
});
const payload =
result.result && typeof result.result === "object"
? (result.result as Record<string, unknown>)
: {};
return NextResponse.json({
...payload,
ok: typeof payload.ok === "boolean" ? payload.ok : true,
meta: {
requestId: result.requestId,
traceId: result.traceId,
commandId: result.commandId,
commandName: result.commandName,
},
});
} catch (error) {
return documentBridgeErrorResponse(error);
}
}
return NextResponse.json({ error: "当前仅支持 Convex 模式" }, { status: 501 });
@@ -98,3 +104,5 @@ export async function POST(request: Request) {
return NextResponse.json({ ok: true });
*/
}
export const runtime = "nodejs";
@@ -1,17 +1,9 @@
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, replaceBlockInTree, withBlocksWrittenBack } from "@/lib/blocks";
import {
assertBlockId,
assertDocumentId,
assertNextBlock,
buildDocumentBridgeContext,
buildDocumentCommandEnvelope,
documentBridgeErrorResponse,
} from "@/lib/documents/bridge";
import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log";
executeBlockPatchCommand,
} from "@/lib/blocks/block-command-adapter";
type PatchPayload = {
sourceDocumentId: string;
@@ -24,78 +16,20 @@ export async function POST(request: Request) {
if (isConvexEnabled()) {
try {
const { sourceDocumentId, workspaceId, blockId, nextBlock }: PatchPayload = await request.json();
const normalizedDocumentId = assertDocumentId(sourceDocumentId);
const normalizedWorkspaceId = workspaceId?.trim() || null;
const normalizedBlockId = assertBlockId(blockId);
assertNextBlock(nextBlock);
const bridgeContext = await buildDocumentBridgeContext({
const result = await executeBlockPatchCommand({
request,
workspaceId: normalizedWorkspaceId,
});
const envelope = buildDocumentCommandEnvelope({
name: "blocks.patch",
payload: {
documentId: normalizedDocumentId,
workspaceId: normalizedWorkspaceId,
blockId: normalizedBlockId,
nextBlock,
},
context: bridgeContext,
target: {
workspaceId: normalizedWorkspaceId,
pageId: normalizedDocumentId,
blockId: normalizedBlockId,
},
});
const { client } = await getAuthedConvexClient();
const doc = await client.query(api.documents.getContent, { id: normalizedDocumentId });
if (!doc) {
return NextResponse.json(
{
error: "页面不存在或无权限",
meta: {
requestId: bridgeContext.requestId,
traceId: bridgeContext.traceId,
commandId: envelope.commandId,
commandName: envelope.name,
},
},
{ status: 404 },
);
}
const blocks = getBlocksFromDocumentContent(doc.content);
const replaced = replaceBlockInTree(blocks, normalizedBlockId, nextBlock as any);
if (!replaced.ok) {
return NextResponse.json(
{
error: "块不存在或无权限",
meta: {
requestId: bridgeContext.requestId,
traceId: bridgeContext.traceId,
commandId: envelope.commandId,
commandName: envelope.name,
},
},
{ status: 404 },
);
}
const payload = withBlocksWrittenBack(doc.content, replaced.nextBlocks);
await client.mutation(api.documents.updateContent, { id: normalizedDocumentId, content: payload });
await recordBridgeCommandArtifacts({
context: bridgeContext,
envelope,
sourceDocumentId,
workspaceId,
blockId,
nextBlock,
});
return NextResponse.json({
ok: true,
meta: {
requestId: bridgeContext.requestId,
traceId: bridgeContext.traceId,
commandId: envelope.commandId,
commandName: envelope.name,
requestId: result.requestId,
traceId: result.traceId,
commandId: result.commandId,
commandName: result.commandName,
},
});
} catch (error) {
@@ -135,3 +69,5 @@ export async function POST(request: Request) {
return NextResponse.json({ ok: true });
*/
}
export const runtime = "nodejs";