diff --git a/.dockerignore b/.dockerignore index 80d8499..23674ef 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,7 @@ node_modules /.svelte-kit /build /data +/library # OS .DS_Store diff --git a/.gitignore b/.gitignore index 310e494..1dbbe60 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ node_modules /.svelte-kit /build /data +/library # OS .DS_Store diff --git a/src/routes/(main)/directory/[[id]]/+page.svelte b/src/routes/(main)/directory/[[id]]/+page.svelte index ff116ec..53f4270 100644 --- a/src/routes/(main)/directory/[[id]]/+page.svelte +++ b/src/routes/(main)/directory/[[id]]/+page.svelte @@ -5,12 +5,14 @@ import CreateBottomSheet from "./CreateBottomSheet.svelte"; import CreateDirectoryModal from "./CreateDirectoryModal.svelte"; import DirectoryEntry from "./DirectoryEntry.svelte"; - import { decryptDirectroyMetadata, requestDirectroyCreation } from "./service"; + import { decryptDirectroyMetadata, requestDirectroyCreation, requestFileUpload } from "./service"; import IconAdd from "~icons/material-symbols/add"; let { data } = $props(); + let fileInput: HTMLInputElement | undefined = $state(); + let isCreateBottomSheetOpen = $state(false); let isCreateDirectoryModalOpen = $state(false); @@ -51,12 +53,21 @@ ); isCreateDirectoryModalOpen = false; }; + + const uploadFile = () => { + const file = fileInput?.files?.[0]; + if (!file) return; + + requestFileUpload(file, data.id, $masterKeyStore?.get(1)!, $clientKeyStore?.signKey!); + }; 파일 + +
{#if data.id !== "root"} {#if !metadata} @@ -95,7 +106,7 @@ }} onFileUpload={() => { isCreateBottomSheetOpen = false; - // TODO + fileInput?.click(); }} /> diff --git a/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte b/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte index 9ea26ce..d079980 100644 --- a/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte +++ b/src/routes/(main)/directory/[[id]]/CreateBottomSheet.svelte @@ -15,7 +15,7 @@ -
+
diff --git a/src/routes/(main)/directory/[[id]]/service.ts b/src/routes/(main)/directory/[[id]]/service.ts index f90477f..8baead9 100644 --- a/src/routes/(main)/directory/[[id]]/service.ts +++ b/src/routes/(main)/directory/[[id]]/service.ts @@ -7,8 +7,14 @@ import { unwrapDataKey, encryptData, decryptData, + digestMessage, + signRequestBody, } from "$lib/modules/crypto"; -import type { DirectroyInfoResponse, DirectoryCreateRequest } from "$lib/server/schemas"; +import type { + DirectroyInfoResponse, + DirectoryCreateRequest, + FileUploadRequest, +} from "$lib/server/schemas"; import type { MasterKey } from "$lib/stores"; export const decryptDirectroyMetadata = async ( @@ -43,3 +49,38 @@ export const requestDirectroyCreation = async ( signKey, ); }; + +export const requestFileUpload = async ( + file: File, + parentId: "root" | number, + masterKey: MasterKey, + signKey: CryptoKey, +) => { + const { dataKey } = await generateDataKey(); + const fileEncrypted = await encryptData(await file.arrayBuffer(), dataKey); + const fileEncryptedHash = await digestMessage(fileEncrypted.ciphertext); + const nameEncrypted = await encryptData(new TextEncoder().encode(file.name), dataKey); + + const form = new FormData(); + form.set( + "metadata", + await signRequestBody( + { + parentId, + mekVersion: masterKey.version, + dek: await wrapDataKey(dataKey, masterKey.key), + contentHash: encodeToBase64(fileEncryptedHash), + contentIv: fileEncrypted.iv, + name: encodeToBase64(nameEncrypted.ciphertext), + nameIv: nameEncrypted.iv, + }, + signKey, + ), + ); + form.set("content", new Blob([fileEncrypted.ciphertext])); + + // TODO: Progress, Scheduling, ... + const xhr = new XMLHttpRequest(); + xhr.open("POST", "/api/file/upload"); + xhr.send(form); +};