diff --git a/excalidraw-app/App.tsx b/excalidraw-app/App.tsx index 438f86a3..a5bff86e 100644 --- a/excalidraw-app/App.tsx +++ b/excalidraw-app/App.tsx @@ -1,7 +1,6 @@ import polyfill from "../packages/excalidraw/polyfill"; import { useCallback, useEffect, useRef, useState } from "react"; import { trackEvent } from "../packages/excalidraw/analytics"; -import { getDefaultAppState } from "../packages/excalidraw/appState"; import { ErrorDialog } from "../packages/excalidraw/components/ErrorDialog"; import { TopErrorBoundary } from "./components/TopErrorBoundary"; import { @@ -55,7 +54,6 @@ import Collab, { isOfflineAtom, } from "./collab/Collab"; import { - exportToBackend, getCollaborationLinkData, isCollaborationLink, loadScene, @@ -663,36 +661,8 @@ const ExcalidrawWrapper = () => { if (exportedElements.length === 0) { throw new Error(t("alerts.cannotExportEmptyCanvas")); } - try { - const { url, errorMessage } = await exportToBackend( - exportedElements, - { - ...appState, - viewBackgroundColor: appState.exportBackground - ? appState.viewBackgroundColor - : getDefaultAppState().viewBackgroundColor, - }, - files, - ); - - if (errorMessage) { - throw new Error(errorMessage); - } - - if (url) { - setLatestShareableLink(url); - } - } catch (error: any) { - if (error.name !== "AbortError") { - const { width, height } = appState; - console.error(error, { - width, - height, - devicePixelRatio: window.devicePixelRatio, - }); - throw new Error(error.message); - } - } + // Not supported as of now + throw new Error(t("alerts.couldNotCreateShareableLink")); }; const renderCustomStats = ( diff --git a/excalidraw-app/data/index.ts b/excalidraw-app/data/index.ts index ba7df82b..c7882294 100644 --- a/excalidraw-app/data/index.ts +++ b/excalidraw-app/data/index.ts @@ -1,41 +1,22 @@ -import { - compressData, - decompressData, -} from "../../packages/excalidraw/data/encode"; -import { - decryptData, - generateEncryptionKey, - IV_LENGTH_BYTES, -} from "../../packages/excalidraw/data/encryption"; -import { serializeAsJSON } from "../../packages/excalidraw/data/json"; +import { generateEncryptionKey } from "../../packages/excalidraw/data/encryption"; import { restore } from "../../packages/excalidraw/data/restore"; import type { ImportedDataState } from "../../packages/excalidraw/data/types"; import type { SceneBounds } from "../../packages/excalidraw/element/bounds"; import { isInvisiblySmallElement } from "../../packages/excalidraw/element/sizeHelpers"; -import { isInitializedImageElement } from "../../packages/excalidraw/element/typeChecks"; import type { ExcalidrawElement, - FileId, OrderedExcalidrawElement, } from "../../packages/excalidraw/element/types"; import { t } from "../../packages/excalidraw/i18n"; import type { AppState, - BinaryFileData, - BinaryFiles, SocketId, UserIdleState, } from "../../packages/excalidraw/types"; import type { MakeBrand } from "../../packages/excalidraw/utility-types"; import { bytesToHexString } from "../../packages/excalidraw/utils"; import type { WS_SUBTYPES } from "../app_constants"; -import { - DELETED_ELEMENT_TIMEOUT, - FILE_UPLOAD_MAX_BYTES, - ROOM_ID_BYTES, -} from "../app_constants"; -import { encodeFilesForUpload } from "./FileManager"; -import { saveFilesToFirebase } from "./firebase"; +import { DELETED_ELEMENT_TIMEOUT, ROOM_ID_BYTES } from "../app_constants"; export type SyncableExcalidrawElement = OrderedExcalidrawElement & MakeBrand<"SyncableExcalidrawElement">; @@ -59,9 +40,6 @@ export const getSyncableElements = ( isSyncableElement(element), ) as SyncableExcalidrawElement[]; -const BACKEND_V2_GET = import.meta.env.VITE_APP_BACKEND_V2_GET_URL; -const BACKEND_V2_POST = import.meta.env.VITE_APP_BACKEND_V2_POST_URL; - const generateRoomId = async () => { const buffer = new Uint8Array(ROOM_ID_BYTES); window.crypto.getRandomValues(buffer); @@ -160,84 +138,6 @@ export const getCollaborationLink = (data: { return `${window.location.origin}${window.location.pathname}#room=${data.roomId},${data.roomKey}`; }; -/** - * Decodes shareLink data using the legacy buffer format. - * @deprecated - */ -const legacy_decodeFromBackend = async ({ - buffer, - decryptionKey, -}: { - buffer: ArrayBuffer; - decryptionKey: string; -}) => { - let decrypted: ArrayBuffer; - - try { - // Buffer should contain both the IV (fixed length) and encrypted data - const iv = buffer.slice(0, IV_LENGTH_BYTES); - const encrypted = buffer.slice(IV_LENGTH_BYTES, buffer.byteLength); - decrypted = await decryptData(new Uint8Array(iv), encrypted, decryptionKey); - } catch (error: any) { - // Fixed IV (old format, backward compatibility) - const fixedIv = new Uint8Array(IV_LENGTH_BYTES); - decrypted = await decryptData(fixedIv, buffer, decryptionKey); - } - - // We need to convert the decrypted array buffer to a string - const string = new window.TextDecoder("utf-8").decode( - new Uint8Array(decrypted), - ); - const data: ImportedDataState = JSON.parse(string); - - return { - elements: data.elements || null, - appState: data.appState || null, - }; -}; - -const importFromBackend = async ( - id: string, - decryptionKey: string, -): Promise => { - try { - const response = await fetch(`${BACKEND_V2_GET}${id}`); - - if (!response.ok) { - window.alert(t("alerts.importBackendFailed")); - return {}; - } - const buffer = await response.arrayBuffer(); - - try { - const { data: decodedBuffer } = await decompressData( - new Uint8Array(buffer), - { - decryptionKey, - }, - ); - const data: ImportedDataState = JSON.parse( - new TextDecoder().decode(decodedBuffer), - ); - - return { - elements: data.elements || null, - appState: data.appState || null, - }; - } catch (error: any) { - console.warn( - "error when decoding shareLink data using the new format:", - error, - ); - return legacy_decodeFromBackend({ buffer, decryptionKey }); - } - } catch (error: any) { - window.alert(t("alerts.importBackendFailed")); - console.error(error); - return {}; - } -}; - export const loadScene = async ( id: string | null, privateKey: string | null, @@ -246,21 +146,9 @@ export const loadScene = async ( // Non-optional so we don't forget to pass it even if `undefined`. localDataState: ImportedDataState | undefined | null, ) => { - let data; - if (id != null && privateKey != null) { - // the private key is used to decrypt the content from the server, take - // extra care not to leak it - data = restore( - await importFromBackend(id, privateKey), - localDataState?.appState, - localDataState?.elements, - { repairBindings: true, refreshDimensions: false }, - ); - } else { - data = restore(localDataState || null, null, null, { - repairBindings: true, - }); - } + const data = restore(localDataState || null, null, null, { + repairBindings: true, + }); return { elements: data.elements, @@ -271,68 +159,3 @@ export const loadScene = async ( files: data.files, }; }; - -type ExportToBackendResult = - | { url: null; errorMessage: string } - | { url: string; errorMessage: null }; - -export const exportToBackend = async ( - elements: readonly ExcalidrawElement[], - appState: Partial, - files: BinaryFiles, -): Promise => { - const encryptionKey = await generateEncryptionKey("string"); - - const payload = await compressData( - new TextEncoder().encode( - serializeAsJSON(elements, appState, files, "database"), - ), - { encryptionKey }, - ); - - try { - const filesMap = new Map(); - for (const element of elements) { - if (isInitializedImageElement(element) && files[element.fileId]) { - filesMap.set(element.fileId, files[element.fileId]); - } - } - - const filesToUpload = await encodeFilesForUpload({ - files: filesMap, - encryptionKey, - maxBytes: FILE_UPLOAD_MAX_BYTES, - }); - - const response = await fetch(BACKEND_V2_POST, { - method: "POST", - body: payload.buffer, - }); - const json = await response.json(); - if (json.id) { - const url = new URL(window.location.href); - // We need to store the key (and less importantly the id) as hash instead - // of queryParam in order to never send it to the server - url.hash = `json=${json.id},${encryptionKey}`; - const urlString = url.toString(); - - await saveFilesToFirebase({ - prefix: `/files/shareLinks/${json.id}`, - files: filesToUpload, - }); - - return { url: urlString, errorMessage: null }; - } else if (json.error_class === "RequestTooLargeError") { - return { - url: null, - errorMessage: t("alerts.couldNotCreateShareableLinkTooBig"), - }; - } - - return { url: null, errorMessage: t("alerts.couldNotCreateShareableLink") }; - } catch (error: any) { - console.error(error); - - return { url: null, errorMessage: t("alerts.couldNotCreateShareableLink") }; - } -};