feat: Add onExportToBackend prop so host can handle it (#2612)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
Aakansha Doshi 2020-12-20 19:44:04 +05:30 committed by GitHub
parent b917e42694
commit 325d1bec91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 164 additions and 117 deletions

View file

@ -1,14 +1,10 @@
import { fileSave } from "browser-nativefs";
import { EVENT_IO, trackEvent } from "../analytics";
import { getDefaultAppState } from "../appState";
import {
copyCanvasToClipboardAsPng,
copyTextToSystemClipboard,
} from "../clipboard";
import {
ExcalidrawElement,
NonDeletedExcalidrawElement,
} from "../element/types";
import { NonDeletedExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { exportToCanvas, exportToSvg } from "../scene/export";
import { ExportType } from "../scene/types";
@ -19,65 +15,6 @@ import { serializeAsJSON } from "./json";
export { loadFromBlob } from "./blob";
export { loadFromJSON, saveAsJSON } from "./json";
const BACKEND_V2_POST = process.env.REACT_APP_BACKEND_V2_POST_URL;
export const exportToBackend = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const json = serializeAsJSON(elements, appState);
const encoded = new TextEncoder().encode(json);
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128,
},
true, // extractable
["encrypt", "decrypt"],
);
// The iv is set to 0. We are never going to reuse the same key so we don't
// need to have an iv. (I hope that's correct...)
const iv = new Uint8Array(12);
// We use symmetric encryption. AES-GCM is the recommended algorithm and
// includes checks that the ciphertext has not been modified by an attacker.
const encrypted = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
key,
encoded,
);
// We use jwk encoding to be able to extract just the base64 encoded key.
// We will hardcode the rest of the attributes when importing back the key.
const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
try {
const response = await fetch(BACKEND_V2_POST, {
method: "POST",
body: encrypted,
});
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},${exportedKey.k!}`;
const urlString = url.toString();
window.prompt(`🔒${t("alerts.uploadedSecurly")}`, urlString);
trackEvent(EVENT_IO, "export", "backend");
} else if (json.error_class === "RequestTooLargeError") {
window.alert(t("alerts.couldNotCreateShareableLinkTooBig"));
} else {
window.alert(t("alerts.couldNotCreateShareableLink"));
}
} catch (error) {
console.error(error);
window.alert(t("alerts.couldNotCreateShareableLink"));
}
};
export const exportCanvas = async (
type: ExportType,
elements: readonly NonDeletedExcalidrawElement[],
@ -169,13 +106,6 @@ export const exportCanvas = async (
}
throw new Error(t("alerts.couldNotCopyToClipboard"));
}
} else if (type === "backend") {
exportToBackend(elements, {
...appState,
viewBackgroundColor: exportBackground
? appState.viewBackgroundColor
: getDefaultAppState().viewBackgroundColor,
});
}
// clean up the DOM