feat: 收口文档桥接与 OnlyOffice/Sidebar 回归

- 为 documents.save/meta/content、blocks.patch 与 sidebar.dataset.list 补齐 Rust 协议映射、共享契约与桥接执行器

- 对齐 BlockNote、Mindmap、OnlyOffice 的保存/路由元信息,并补真实浏览器回归脚本与 OnlyOffice 部署基线

- 忽略 Rust 本地构建产物与 Harness 调试状态文件,避免临时产物进入仓库历史
This commit is contained in:
lix-2026
2026-04-15 03:06:29 +08:00
parent 84a8454fa9
commit b33ffb99e7
51 changed files with 3260 additions and 379 deletions
@@ -44,6 +44,14 @@ export async function GET(request: Request) {
return NextResponse.json({
content: result.content ?? null,
revision:
typeof result.revision === "number" && Number.isInteger(result.revision)
? result.revision
: 0,
conflictDetectionKey:
typeof result.conflict_detection_key === "string" && result.conflict_detection_key.trim()
? result.conflict_detection_key
: `${documentId}:0`,
meta: {
requestId: bridgeContext.requestId,
traceId: bridgeContext.traceId,
@@ -1,58 +1,54 @@
import { NextResponse } from "next/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { api } from "@/lib/convex/api";
import {
assertDocumentId,
buildDocumentBridgeContext,
buildDocumentCommandEnvelope,
documentBridgeErrorResponse,
} from "@/lib/documents/bridge";
import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log";
interface SavePayload {
documentId: string;
workspaceId?: string | null;
content: unknown;
}
import { executeSaveBridgeCommand } from "@/lib/documents/save-command-adapter";
import {
buildDocumentSavePayload,
type DocumentSavePayload,
} from "@/lib/documents/save-contract";
export async function POST(request: Request) {
if (isConvexEnabled()) {
try {
const { documentId, workspaceId, content }: SavePayload = await request.json();
const normalizedDocumentId = assertDocumentId(documentId);
const normalizedWorkspaceId = workspaceId?.trim() || null;
const body = await request.json() as Partial<DocumentSavePayload> & { content: unknown };
const normalizedDocumentId = assertDocumentId(body.documentId);
const payload = buildDocumentSavePayload({
documentId: normalizedDocumentId,
workspaceId: body.workspaceId,
revision: body.revision,
content: body.content as DocumentSavePayload["content"],
conflictDetectionKey: body.conflictDetectionKey,
});
const normalizedWorkspaceId = payload.workspaceId;
const bridgeContext = await buildDocumentBridgeContext({ request, workspaceId: normalizedWorkspaceId });
const envelope = buildDocumentCommandEnvelope({
name: "documents.save",
payload: {
documentId: normalizedDocumentId,
workspaceId: normalizedWorkspaceId,
content,
},
payload: payload satisfies DocumentSavePayload,
context: bridgeContext,
target: {
workspaceId: normalizedWorkspaceId,
pageId: normalizedDocumentId,
},
});
const { client } = await getAuthedConvexClient();
await client.mutation(api.documents.updateContent, {
id: normalizedDocumentId,
content: envelope.payload.content,
});
await recordBridgeCommandArtifacts({
const result = await executeSaveBridgeCommand({
context: bridgeContext,
envelope,
});
return NextResponse.json({
ok: true,
revision: result.revision,
conflictDetectionKey: result.conflictDetectionKey,
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) {