88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { api } from "@/lib/convex/api";
|
|
import { HttpError } from "@/lib/auth/authContext";
|
|
import { getAuthedConvexClient } from "@/lib/convex/route";
|
|
import { resolveOnlyOfficeInternalUrl } from "@/lib/onlyoffice/internal-url";
|
|
import { documentBridgeErrorResponse } from "@/lib/documents/bridge";
|
|
import { prepareOnlyOfficeForcesave } from "@/lib/onlyoffice/rust-adapter";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function POST(request: Request) {
|
|
const onlyofficeInternalUrl = await resolveOnlyOfficeInternalUrl();
|
|
let auth;
|
|
let client;
|
|
try {
|
|
const authed = await getAuthedConvexClient();
|
|
auth = authed.auth;
|
|
client = authed.client;
|
|
} catch (err) {
|
|
if (err instanceof HttpError) {
|
|
return NextResponse.json({ error: "未登录" }, { status: err.status });
|
|
}
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
}
|
|
|
|
const { searchParams } = new URL(request.url);
|
|
const assetId = String(searchParams.get("assetId") || "").trim();
|
|
const key = String(searchParams.get("key") || "").trim();
|
|
if (!assetId) return NextResponse.json({ error: "缺少 assetId" }, { status: 400 });
|
|
if (!key) return NextResponse.json({ error: "缺少 key" }, { status: 400 });
|
|
|
|
const asset = await client.query(api.mediaAssets.getById, { userId: auth.userId, id: assetId });
|
|
if (!asset) {
|
|
return NextResponse.json({ error: "资源不存在" }, { status: 404 });
|
|
}
|
|
|
|
// 只读权限:不允许触发 forcesave(避免只读用户间接写回存储)
|
|
const docId = String((asset as any).document_id || "").trim();
|
|
if (!docId) {
|
|
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
|
|
}
|
|
const doc = await client.query(api.documents.getMeta, { id: docId });
|
|
if (!doc || (doc as any).can_edit === false) {
|
|
return NextResponse.json({ error: "无权修改(只读共享的文件)" }, { status: 403 });
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const tryJson = async (res: Response) => (await res.json().catch(() => null)) as any;
|
|
|
|
try {
|
|
const prepared = await prepareOnlyOfficeForcesave({
|
|
request,
|
|
assetId,
|
|
key,
|
|
onlyofficeInternalUrl,
|
|
secret: process.env.ONLYOFFICE_JWT_SECRET || "",
|
|
actorId: auth.userId,
|
|
documentId: docId,
|
|
workspaceId: String((asset as any).workspace_id || "").trim() || null,
|
|
});
|
|
|
|
const errors: Record<string, unknown> = {};
|
|
for (const outbound of prepared.requests) {
|
|
const response = await fetch(outbound.url, {
|
|
method: outbound.method,
|
|
headers: Object.fromEntries(outbound.headers.map((item) => [item.name, item.value])),
|
|
body: outbound.bodyJson,
|
|
});
|
|
const payload = await tryJson(response);
|
|
if (response.ok && Number(payload?.error ?? 0) === 0) {
|
|
return NextResponse.json({ ok: true, via: outbound.via, result: payload });
|
|
}
|
|
errors[outbound.via] = payload;
|
|
}
|
|
|
|
return NextResponse.json(
|
|
{ error: "触发 forcesave 失败", detail: errors },
|
|
{ status: 502 },
|
|
);
|
|
} catch (error) {
|
|
if (error instanceof Error && error.name === "DocumentBridgeError") {
|
|
return documentBridgeErrorResponse(error);
|
|
}
|
|
console.error("[onlyoffice/forcesave] failed:", error);
|
|
return NextResponse.json({ error: "触发 forcesave 失败" }, { status: 502 });
|
|
}
|
|
}
|