Skip to content

Commit e0c97c5

Browse files
refactor: sanitize HTML function (#8307)
* refactor: replace isomorphic-dompurify with sanitize-html * dompurify fixes * more fixes with fallback and title * build --------- Co-authored-by: Prateek Shourya <[email protected]>
1 parent 76ebf39 commit e0c97c5

File tree

14 files changed

+235
-260
lines changed

14 files changed

+235
-260
lines changed

apps/live/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
"express-ws": "^5.0.2",
4646
"helmet": "^7.1.0",
4747
"ioredis": "5.7.0",
48-
"isomorphic-dompurify": "^1.8.0",
4948
"uuid": "catalog:",
5049
"ws": "^8.18.3",
5150
"y-prosemirror": "^1.3.7",

apps/live/src/extensions/database.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,32 @@ const fetchDocument = async ({ context, documentName: pageId, instance }: FetchP
2020
try {
2121
const service = getPageService(context.documentType, context);
2222
// fetch details
23-
const response = await service.fetchDescriptionBinary(pageId);
23+
const response = (await service.fetchDescriptionBinary(pageId)) as Buffer;
2424
const binaryData = new Uint8Array(response);
2525
// if binary data is empty, convert HTML to binary data
2626
if (binaryData.byteLength === 0) {
2727
const pageDetails = await service.fetchDetails(pageId);
28-
const convertedBinaryData = getBinaryDataFromDocumentEditorHTMLString(pageDetails.description_html ?? "<p></p>");
28+
const convertedBinaryData = getBinaryDataFromDocumentEditorHTMLString(
29+
pageDetails.description_html ?? "<p></p>",
30+
pageDetails.name
31+
);
2932
if (convertedBinaryData) {
3033
// save the converted binary data back to the database
31-
const { contentBinaryEncoded, contentHTML, contentJSON } = getAllDocumentFormatsFromDocumentEditorBinaryData(
32-
convertedBinaryData,
33-
true
34-
);
35-
const payload = {
36-
description_binary: contentBinaryEncoded,
37-
description_html: contentHTML,
38-
description: contentJSON,
39-
};
40-
await service.updateDescriptionBinary(pageId, payload);
34+
try {
35+
const { contentBinaryEncoded, contentHTML, contentJSON } = getAllDocumentFormatsFromDocumentEditorBinaryData(
36+
convertedBinaryData,
37+
true
38+
);
39+
const payload = {
40+
description_binary: contentBinaryEncoded,
41+
description_html: contentHTML,
42+
description: contentJSON,
43+
};
44+
await service.updateDescriptionBinary(pageId, payload);
45+
} catch (e) {
46+
const error = new AppError(e);
47+
logger.error("Failed to save binary after first convertion from html:", error);
48+
}
4149
return convertedBinaryData;
4250
}
4351
}

apps/live/src/extensions/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { Database } from "./database";
2+
import { ForceCloseHandler } from "./force-close-handler";
23
import { Logger } from "./logger";
34
import { Redis } from "./redis";
5+
import { TitleSyncExtension } from "./title-sync";
46

5-
export const getExtensions = () => [new Logger(), new Database(), new Redis()];
7+
export const getExtensions = () => [
8+
new Logger(),
9+
new Database(),
10+
new Redis(),
11+
new TitleSyncExtension(),
12+
new ForceCloseHandler(), // Must be after Redis to receive broadcasts
13+
];

apps/live/src/extensions/title-sync.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
// hocuspocus
22
import type { Extension, Hocuspocus, Document } from "@hocuspocus/server";
33
import { TiptapTransformer } from "@hocuspocus/transformer";
4+
import type { AnyExtension, JSONContent } from "@tiptap/core";
45
import type * as Y from "yjs";
56
// editor extensions
6-
import { TITLE_EDITOR_EXTENSIONS, createRealtimeEvent } from "@plane/editor";
7+
import {
8+
TITLE_EDITOR_EXTENSIONS,
9+
createRealtimeEvent,
10+
extractTextFromHTML,
11+
generateTitleProsemirrorJson,
12+
} from "@plane/editor";
713
import { logger } from "@plane/logger";
814
import { AppError } from "@/lib/errors";
915
// helpers
1016
import { getPageService } from "@/services/page/handler";
1117
import type { HocusPocusServerContext, OnLoadDocumentPayloadWithContext } from "@/types";
12-
import { generateTitleProsemirrorJson } from "@/utils";
1318
import { broadcastMessageToPage } from "@/utils/broadcast-message";
1419
import { TitleUpdateManager } from "./title-update/title-update-manager";
15-
import { extractTextFromHTML } from "./title-update/title-utils";
1620

1721
/**
1822
* Hocuspocus extension for synchronizing document titles
@@ -41,15 +45,11 @@ export class TitleSyncExtension implements Extension {
4145
// in the yjs binary
4246
if (document.isEmpty("title")) {
4347
const service = getPageService(context.documentType, context);
44-
// const title = await service.fe
45-
const title = (await service.fetchDetails?.(documentName)).name;
48+
const pageDetails = await service.fetchDetails(documentName);
49+
const title = pageDetails.name;
4650
if (title == null) return;
47-
const titleField = TiptapTransformer.toYdoc(
48-
generateTitleProsemirrorJson(title),
49-
"title",
50-
// editor
51-
TITLE_EDITOR_EXTENSIONS as any
52-
);
51+
const titleJson = (generateTitleProsemirrorJson as (text: string) => JSONContent)(title);
52+
const titleField = TiptapTransformer.toYdoc(titleJson, "title", TITLE_EDITOR_EXTENSIONS as AnyExtension[]);
5353
document.merge(titleField);
5454
}
5555
} catch (error) {

apps/live/src/extensions/title-update/title-utils.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

apps/live/src/utils/document.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

apps/live/src/utils/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

apps/web/core/components/pages/editor/page-root.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const PageRoot = observer((props: TPageRootProps) => {
6969
const { isFetchingFallbackBinary } = usePageFallback({
7070
editorRef,
7171
fetchPageDescription: handlers.fetchDescriptionBinary,
72+
page,
7273
collaborationState,
7374
updatePageDescription: handlers.updateDescription,
7475
});

apps/web/core/hooks/use-page-fallback.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ import { useCallback, useEffect, useRef, useState } from "react";
22
import type { EditorRefApi, CollaborationState } from "@plane/editor";
33
// plane editor
44
import { convertBinaryDataToBase64String, getBinaryDataFromDocumentEditorHTMLString } from "@plane/editor";
5-
// plane propel
6-
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
75
// plane types
86
import type { TDocumentPayload } from "@plane/types";
97
// hooks
108
import useAutoSave from "@/hooks/use-auto-save";
9+
import type { TPageInstance } from "@/store/pages/base-page";
1110

1211
type TArgs = {
1312
editorRef: React.RefObject<EditorRefApi>;
1413
fetchPageDescription: () => Promise<ArrayBuffer>;
1514
collaborationState: CollaborationState | null;
1615
updatePageDescription: (data: TDocumentPayload) => Promise<void>;
16+
page: TPageInstance;
1717
};
1818

1919
export const usePageFallback = (args: TArgs) => {
20-
const { editorRef, fetchPageDescription, collaborationState, updatePageDescription } = args;
20+
const { editorRef, fetchPageDescription, collaborationState, updatePageDescription, page } = args;
2121
const hasShownFallbackToast = useRef(false);
2222

2323
const [isFetchingFallbackBinary, setIsFetchingFallbackBinary] = useState(false);
@@ -32,12 +32,7 @@ export const usePageFallback = (args: TArgs) => {
3232

3333
// Show toast notification when fallback mechanism kicks in (only once)
3434
if (!hasShownFallbackToast.current) {
35-
// setToast({
36-
// type: TOAST_TYPE.WARNING,
37-
// title: "Connection lost",
38-
// message: "Your changes are being saved using backup mechanism. ",
39-
// });
40-
console.log("Connection lost");
35+
console.warn("Websocket Connection lost, your changes are being saved using backup mechanism.");
4136
hasShownFallbackToast.current = true;
4237
}
4338

@@ -49,7 +44,11 @@ export const usePageFallback = (args: TArgs) => {
4944
if (latestEncodedDescription && latestEncodedDescription.byteLength > 0) {
5045
latestDecodedDescription = new Uint8Array(latestEncodedDescription);
5146
} else {
52-
latestDecodedDescription = getBinaryDataFromDocumentEditorHTMLString("<p></p>");
47+
const pageDescriptionHtml = page.description_html;
48+
latestDecodedDescription = getBinaryDataFromDocumentEditorHTMLString(
49+
pageDescriptionHtml ?? "<p></p>",
50+
page.name
51+
);
5352
}
5453

5554
editor.setProviderDocument(latestDecodedDescription);
@@ -64,15 +63,10 @@ export const usePageFallback = (args: TArgs) => {
6463
});
6564
} catch (error: any) {
6665
console.error(error);
67-
// setToast({
68-
// type: TOAST_TYPE.ERROR,
69-
// title: "Error",
70-
// message: `Failed to update description using backup mechanism, ${error?.message}`,
71-
// });
7266
} finally {
7367
setIsFetchingFallbackBinary(false);
7468
}
75-
}, [editorRef, fetchPageDescription, hasConnectionFailed, updatePageDescription]);
69+
}, [editorRef, fetchPageDescription, hasConnectionFailed, updatePageDescription, page.description_html, page.name]);
7670

7771
useEffect(() => {
7872
if (hasConnectionFailed) {

packages/editor/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
"emoji-regex": "^10.3.0",
6767
"highlight.js": "^11.8.0",
6868
"is-emoji-supported": "^0.0.5",
69-
"isomorphic-dompurify": "^1.8.0",
7069
"jsx-dom-cjs": "^8.0.3",
7170
"linkifyjs": "^4.3.2",
7271
"lowlight": "^3.0.0",

0 commit comments

Comments
 (0)