fix: 修复 Rust OnlyOffice 附件打开链路

This commit is contained in:
lix-2026
2026-05-11 08:27:52 +08:00
parent 2f9ef85350
commit b7dddd2a66
20 changed files with 3484 additions and 109 deletions
+1 -3
View File
@@ -1,5 +1,4 @@
import { Suspense } from "react";
import Script from "next/script";
import OnlyOfficeClientPage from "./OnlyOfficeClientPage";
export const dynamic = "force-dynamic";
@@ -105,9 +104,8 @@ export default function OnlyOfficePage() {
</div>
}
>
<Script
<script
id="mnote-onlyoffice-earlypatch"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{ __html: ONLYOFFICE_EARLY_PATCH }}
/>
<OnlyOfficeClientPage />
@@ -1,9 +1,11 @@
import { describe, expect, it } from "vitest";
import {
buildOnlyOfficeAssetOpenUrl,
buildOnlyOfficeCallbackUrl,
buildOnlyOfficeForcesaveUrl,
buildOnlyOfficeOpenFileId,
docTypeFromExt,
inferOnlyOfficeFileType,
resolveOnlyOfficeDocumentUrl,
} from "@/lib/onlyoffice/client-session";
@@ -15,6 +17,29 @@ describe("onlyoffice client session helpers", () => {
expect(docTypeFromExt("pdf")).toBe("pdf");
});
it("inferOnlyOfficeFileType detects Office assets from file name and MIME", () => {
expect(inferOnlyOfficeFileType("demo.pptx", null)).toBe("pptx");
expect(inferOnlyOfficeFileType("demo", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")).toBe("docx");
expect(inferOnlyOfficeFileType("demo", "application/pdf")).toBe("pdf");
expect(inferOnlyOfficeFileType("demo.png", "image/png")).toBeNull();
});
it("buildOnlyOfficeAssetOpenUrl keeps attachment identity for callback writeback", () => {
const result = buildOnlyOfficeAssetOpenUrl({
origin: "https://mnote.example.com",
fileUrl: "https://storage.example.com/demo.pptx?token=abc",
fileName: "2023自我介绍PPT_李爱波0831.pptx",
fileType: "pptx",
assetId: "asset_ppt_1",
documentId: "doc_1",
mode: "edit",
});
expect(result).toBe(
"https://mnote.example.com/onlyoffice?fileUrl=https%3A%2F%2Fstorage.example.com%2Fdemo.pptx%3Ftoken%3Dabc&fileName=2023%E8%87%AA%E6%88%91%E4%BB%8B%E7%BB%8DPPT_%E6%9D%8E%E7%88%B1%E6%B3%A20831.pptx&fileType=pptx&assetId=asset_ppt_1&documentId=doc_1&mode=edit",
);
});
it("resolveOnlyOfficeDocumentUrl wraps signed url with proxy", () => {
const result = resolveOnlyOfficeDocumentUrl({
effectiveFileUrl: "https://public.example.com/storage/v1/object/sign/documents/a.docx?token=abc",
@@ -30,6 +30,40 @@ export const docTypeFromExt = (ext: string) => {
return "word";
};
export const inferOnlyOfficeFileType = (fileName: string | null | undefined, mimeType: string | null | undefined) => {
const name = (fileName ?? "").trim().toLowerCase();
const mt = (mimeType ?? "").trim().toLowerCase();
const ext = name.includes(".") ? name.split(".").pop() ?? "" : "";
if (["doc", "docx", "odt", "rtf"].includes(ext)) return ext;
if (["ppt", "pptx", "odp"].includes(ext)) return ext;
if (["xls", "xlsx", "ods", "csv"].includes(ext)) return ext;
if (ext === "pdf") return ext;
if (mt.includes("wordprocessingml")) return "docx";
if (mt.includes("presentationml")) return "pptx";
if (mt.includes("spreadsheetml")) return "xlsx";
if (mt.includes("pdf")) return "pdf";
return null;
};
export function buildOnlyOfficeAssetOpenUrl(input: {
origin: string;
fileUrl: string;
fileName: string;
fileType: string;
assetId?: string | null;
documentId?: string | null;
mode?: "view" | "edit";
}): string {
const target = new URL("/onlyoffice", input.origin);
target.searchParams.set("fileUrl", input.fileUrl);
target.searchParams.set("fileName", input.fileName);
target.searchParams.set("fileType", input.fileType);
if (input.assetId) target.searchParams.set("assetId", input.assetId);
if (input.documentId) target.searchParams.set("documentId", input.documentId);
target.searchParams.set("mode", input.mode ?? "edit");
return target.toString();
}
export function resolveOnlyOfficeDocumentUrl(input: {
effectiveFileUrl: string;
proxyOrigin: string;