0.2.1 onlyoffice修复

This commit is contained in:
liaibo
2026-01-17 10:12:53 +08:00
parent 94957dc361
commit 19907bccdc
102 changed files with 7188 additions and 186 deletions
@@ -0,0 +1,116 @@
import { randomUUID } from "crypto";
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { requireAuthContext } from "@/lib/auth/authContext";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, findBlockInTree, withBlocksWrittenBack } from "@/lib/blocks";
type EmbedBlockPayload = {
sourceDocumentId: string;
blockId: string;
targetDocumentId: string;
position?: "end";
};
export async function POST(request: Request) {
const { sourceDocumentId, blockId, targetDocumentId }: EmbedBlockPayload = await request.json();
if (!sourceDocumentId || !blockId || !targetDocumentId) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
}
if (sourceDocumentId === targetDocumentId) {
return NextResponse.json({ error: "禁止嵌入到当前页面" }, { status: 400 });
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const client = getConvexHttpClient();
const source = await client.query(api.documents.getContent, { userId: auth.userId, 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, { userId: auth.userId, id: targetDocumentId });
if (!target) return NextResponse.json({ error: "目标页面不存在或无权限" }, { status: 404 });
const targetBlocks = getBlocksFromDocumentContent(target.content);
const referenceBlock = {
id: randomUUID(),
type: "blockReference",
props: {
sourceDocumentId,
targetBlockId: blockId,
display: "embed",
},
content: [],
children: [],
};
const nextBlocks = [...targetBlocks, referenceBlock];
const payload = withBlocksWrittenBack(target.content, nextBlocks);
await client.mutation(api.documents.updateContent, { userId: auth.userId, id: targetDocumentId, content: payload });
return NextResponse.json({ ok: true });
}
const supabase = await createSupabaseRouteClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) {
return NextResponse.json({ error: "未登录" }, { status: 401 });
}
const { data: sourceDoc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id)
.single();
if (!sourceDoc) return NextResponse.json({ error: "源页面不存在或无权限" }, { status: 404 });
const sourceBlocks = getBlocksFromDocumentContent(sourceDoc.content);
if (!findBlockInTree(sourceBlocks, blockId)) {
return NextResponse.json({ error: "源块不存在或无权限" }, { status: 404 });
}
const { data: targetDoc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", targetDocumentId)
.eq("user_id", session.user.id)
.single();
if (!targetDoc) return NextResponse.json({ error: "目标页面不存在或无权限" }, { status: 404 });
const targetBlocks = getBlocksFromDocumentContent(targetDoc.content);
const referenceBlock = {
id: randomUUID(),
type: "blockReference",
props: {
sourceDocumentId,
targetBlockId: blockId,
display: "embed",
},
content: [],
children: [],
};
const nextBlocks = [...targetBlocks, referenceBlock];
const payload = withBlocksWrittenBack(targetDoc.content, nextBlocks);
const { error } = await supabase
.from("documents")
.update({ content: payload })
.eq("id", targetDocumentId)
.eq("user_id", session.user.id);
if (error) return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json({ ok: true });
}
@@ -0,0 +1,49 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { requireAuthContext } from "@/lib/auth/authContext";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, findBlockInTree } from "@/lib/blocks";
export async function GET(request: Request) {
const url = new URL(request.url);
const sourceDocumentId = url.searchParams.get("sourceDocumentId") || "";
const blockId = url.searchParams.get("blockId") || "";
if (!sourceDocumentId || !blockId) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const client = getConvexHttpClient();
const doc = await client.query(api.documents.getContent, { userId: auth.userId, 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 });
}
const supabase = await createSupabaseRouteClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) return NextResponse.json({ error: "未登录" }, { status: 401 });
const { data: doc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id)
.single();
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 });
}
@@ -0,0 +1,100 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { requireAuthContext } from "@/lib/auth/authContext";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, removeBlockSubtree, withBlocksWrittenBack } from "@/lib/blocks";
type MoveBlockPayload = {
sourceDocumentId: string;
blockId: string;
targetDocumentId: string;
position?: "end";
};
export async function POST(request: Request) {
const { sourceDocumentId, blockId, targetDocumentId }: MoveBlockPayload = await request.json();
if (!sourceDocumentId || !blockId || !targetDocumentId) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
}
if (sourceDocumentId === targetDocumentId) {
// 说明:同页移动先不做“定位插入”,视为 no-op。
return NextResponse.json({ ok: true, noop: true });
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const client = getConvexHttpClient();
const source = await client.query(api.documents.getContent, { userId: auth.userId, 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, { userId: auth.userId, 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, { userId: auth.userId, id: sourceDocumentId, content: nextSourcePayload });
await client.mutation(api.documents.updateContent, { userId: auth.userId, id: targetDocumentId, content: nextTargetPayload });
return NextResponse.json({ ok: true });
}
const supabase = await createSupabaseRouteClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) {
return NextResponse.json({ error: "未登录" }, { status: 401 });
}
const { data: sourceDoc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id)
.single();
if (!sourceDoc) return NextResponse.json({ error: "源页面不存在或无权限" }, { status: 404 });
const sourceBlocks = getBlocksFromDocumentContent(sourceDoc.content);
const removedRes = removeBlockSubtree(sourceBlocks, blockId);
if (!removedRes.removed) return NextResponse.json({ error: "源块不存在或无权限" }, { status: 404 });
const { data: targetDoc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", targetDocumentId)
.eq("user_id", session.user.id)
.single();
if (!targetDoc) return NextResponse.json({ error: "目标页面不存在或无权限" }, { status: 404 });
const targetBlocks = getBlocksFromDocumentContent(targetDoc.content);
const nextSourcePayload = withBlocksWrittenBack(sourceDoc.content, removedRes.nextBlocks);
const nextTargetPayload = withBlocksWrittenBack(targetDoc.content, [...targetBlocks, removedRes.removed]);
const { error: srcErr } = await supabase
.from("documents")
.update({ content: nextSourcePayload })
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id);
if (srcErr) return NextResponse.json({ error: srcErr.message }, { status: 500 });
const { error: tgtErr } = await supabase
.from("documents")
.update({ content: nextTargetPayload })
.eq("id", targetDocumentId)
.eq("user_id", session.user.id);
if (tgtErr) return NextResponse.json({ error: tgtErr.message }, { status: 500 });
return NextResponse.json({ ok: true });
}
@@ -0,0 +1,65 @@
import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import { isConvexEnabled } from "@/lib/convex/enabled";
import { requireAuthContext } from "@/lib/auth/authContext";
import { getConvexHttpClient } from "@/lib/convex/server";
import { api } from "@/lib/convex/api";
import { getBlocksFromDocumentContent, replaceBlockInTree, withBlocksWrittenBack } from "@/lib/blocks";
type PatchPayload = {
sourceDocumentId: string;
blockId: string;
nextBlock: unknown;
};
export async function POST(request: Request) {
const { sourceDocumentId, blockId, nextBlock }: PatchPayload = await request.json();
if (!sourceDocumentId || !blockId || !nextBlock) {
return NextResponse.json({ error: "缺少参数" }, { status: 400 });
}
if (isConvexEnabled()) {
const auth = requireAuthContext();
const client = getConvexHttpClient();
const doc = await client.query(api.documents.getContent, { userId: auth.userId, id: sourceDocumentId });
if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 });
const blocks = getBlocksFromDocumentContent(doc.content);
const replaced = replaceBlockInTree(blocks, blockId, nextBlock as any);
if (!replaced.ok) return NextResponse.json({ error: "块不存在或无权限" }, { status: 404 });
const payload = withBlocksWrittenBack(doc.content, replaced.nextBlocks);
await client.mutation(api.documents.updateContent, { userId: auth.userId, id: sourceDocumentId, content: payload });
return NextResponse.json({ ok: true });
}
const supabase = await createSupabaseRouteClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) return NextResponse.json({ error: "未登录" }, { status: 401 });
const { data: doc } = await supabase
.from("documents")
.select("id,content,user_id")
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id)
.single();
if (!doc) return NextResponse.json({ error: "页面不存在或无权限" }, { status: 404 });
const blocks = getBlocksFromDocumentContent(doc.content);
const replaced = replaceBlockInTree(blocks, blockId, nextBlock as any);
if (!replaced.ok) return NextResponse.json({ error: "块不存在或无权限" }, { status: 404 });
const payload = withBlocksWrittenBack(doc.content, replaced.nextBlocks);
const { error } = await supabase
.from("documents")
.update({ content: payload })
.eq("id", sourceDocumentId)
.eq("user_id", session.user.id);
if (error) return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json({ ok: true });
}