mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-11-04 08:10:28 +09:00
암호화 마이그레이션 기능 삭제
This commit is contained in:
1 parent
3c1e98c872
commit
c2874035ba
13 files changed
+141
-589
No files matched your search
@@ -1,7 +0,0 @@
|
||||
import { createCaller } from "$trpc/router.server";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
const files = await createCaller(event).file.listLegacy();
|
||||
return { files };
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { BottomDiv, Button, FullscreenDiv } from "$lib/components/atoms";
|
||||
import { TopBar } from "$lib/components/molecules";
|
||||
import type { MaybeFileInfo } from "$lib/modules/filesystem";
|
||||
import { masterKeyStore } from "$lib/stores";
|
||||
import { sortEntries } from "$lib/utils";
|
||||
import File from "./File.svelte";
|
||||
import {
|
||||
getMigrationState,
|
||||
clearMigrationStates,
|
||||
requestLegacyFiles,
|
||||
requestFileMigration,
|
||||
} from "./service.svelte";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let fileInfos: MaybeFileInfo[] = $state([]);
|
||||
let files = $derived(
|
||||
fileInfos
|
||||
.map((info) => ({
|
||||
info,
|
||||
state: getMigrationState(info.id),
|
||||
}))
|
||||
.filter((file) => file.state?.status !== "uploaded"),
|
||||
);
|
||||
|
||||
const migrateAllFiles = () => {
|
||||
files.forEach(({ info }) => {
|
||||
if (info.exists) {
|
||||
requestFileMigration(info);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
fileInfos = sortEntries(await requestLegacyFiles(data.files, $masterKeyStore?.get(1)?.key!));
|
||||
});
|
||||
|
||||
$effect(() => clearMigrationStates);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>암호화 마이그레이션</title>
|
||||
</svelte:head>
|
||||
|
||||
<TopBar title="암호화 마이그레이션" />
|
||||
<FullscreenDiv>
|
||||
{#if files.length > 0}
|
||||
<div class="space-y-4 pb-4">
|
||||
<p class="break-keep text-gray-800">
|
||||
이전 버전의 ArkVault에서 업로드된 {files.length}개 파일을 다시 암호화할 수 있어요.
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
{#each files as { info, state } (info.id)}
|
||||
{#if info.exists}
|
||||
<File
|
||||
{info}
|
||||
{state}
|
||||
onclick={({ id }) => goto(`/file/${id}`)}
|
||||
onMigrateClick={requestFileMigration}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<BottomDiv>
|
||||
<Button onclick={migrateAllFiles} class="w-full">모두 다시 암호화하기</Button>
|
||||
</BottomDiv>
|
||||
{:else}
|
||||
<div class="flex flex-grow items-center justify-center">
|
||||
<p class="text-gray-500">
|
||||
{#if data.files.length === 0}
|
||||
마이그레이션할 파일이 없어요.
|
||||
{:else}
|
||||
파일 목록을 불러오고 있어요.
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</FullscreenDiv>
|
||||
@@ -1,52 +0,0 @@
|
||||
<script module lang="ts">
|
||||
const subtexts = {
|
||||
queued: "대기 중",
|
||||
downloading: "다운로드하는 중",
|
||||
"upload-pending": "업로드를 기다리는 중",
|
||||
uploaded: "",
|
||||
error: "실패",
|
||||
} as const;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { ActionEntryButton } from "$lib/components/atoms";
|
||||
import { DirectoryEntryLabel } from "$lib/components/molecules";
|
||||
import type { FileInfo } from "$lib/modules/filesystem";
|
||||
import { formatDateTime, formatNetworkSpeed } from "$lib/utils";
|
||||
import type { MigrationState } from "./service.svelte";
|
||||
|
||||
import IconSync from "~icons/material-symbols/sync";
|
||||
|
||||
type FileInfoWithExists = FileInfo & { exists: true };
|
||||
|
||||
interface Props {
|
||||
info: FileInfoWithExists;
|
||||
onclick: (file: FileInfo) => void;
|
||||
onMigrateClick: (file: FileInfoWithExists) => void;
|
||||
state: MigrationState | undefined;
|
||||
}
|
||||
|
||||
let { info, onclick, onMigrateClick, state }: Props = $props();
|
||||
|
||||
let subtext = $derived.by(() => {
|
||||
if (!state) {
|
||||
return formatDateTime(info.createdAt ?? info.lastModifiedAt);
|
||||
}
|
||||
if (state.status === "uploading") {
|
||||
const progress = Math.floor((state.progress ?? 0) * 100);
|
||||
const speed = formatNetworkSpeed((state.rate ?? 0) * 8);
|
||||
return `전송됨 ${progress}% · ${speed}`;
|
||||
}
|
||||
return subtexts[state.status] ?? state.status;
|
||||
});
|
||||
</script>
|
||||
|
||||
<ActionEntryButton
|
||||
class="h-14"
|
||||
onclick={() => onclick(info)}
|
||||
actionButtonIcon={!state || state.status === "error" ? IconSync : undefined}
|
||||
onActionButtonClick={() => onMigrateClick(info)}
|
||||
actionButtonClass="text-gray-800"
|
||||
>
|
||||
<DirectoryEntryLabel type="file" name={info.name} {subtext} />
|
||||
</ActionEntryButton>
|
||||
@@ -1,116 +0,0 @@
|
||||
import { limitFunction } from "p-limit";
|
||||
import { SvelteMap } from "svelte/reactivity";
|
||||
import { CHUNK_SIZE } from "$lib/constants";
|
||||
import { getFileInfo, type FileInfo } from "$lib/modules/filesystem";
|
||||
import { uploadBlob } from "$lib/modules/upload";
|
||||
import { requestFileDownload } from "$lib/services/file";
|
||||
import { HybridPromise, Scheduler } from "$lib/utils";
|
||||
import { trpc } from "$trpc/client";
|
||||
import type { RouterOutputs } from "$trpc/router.server";
|
||||
|
||||
export type MigrationStatus =
|
||||
| "queued"
|
||||
| "downloading"
|
||||
| "upload-pending"
|
||||
| "uploading"
|
||||
| "uploaded"
|
||||
| "error";
|
||||
|
||||
export interface MigrationState {
|
||||
status: MigrationStatus;
|
||||
progress?: number;
|
||||
rate?: number;
|
||||
}
|
||||
|
||||
const scheduler = new Scheduler();
|
||||
const states = new SvelteMap<number, MigrationState>();
|
||||
|
||||
export const requestLegacyFiles = async (
|
||||
filesRaw: RouterOutputs["file"]["listLegacy"],
|
||||
masterKey: CryptoKey,
|
||||
) => {
|
||||
const files = await HybridPromise.all(
|
||||
filesRaw.map((file) => getFileInfo(file.id, masterKey, { serverResponse: file })),
|
||||
);
|
||||
return files;
|
||||
};
|
||||
|
||||
const createState = (status: MigrationStatus): MigrationState => {
|
||||
const state = $state({ status });
|
||||
return state;
|
||||
};
|
||||
|
||||
export const getMigrationState = (fileId: number) => {
|
||||
return states.get(fileId);
|
||||
};
|
||||
|
||||
export const clearMigrationStates = () => {
|
||||
for (const [id, state] of states) {
|
||||
if (state.status === "uploaded" || state.status === "error") {
|
||||
states.delete(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requestFileUpload = limitFunction(
|
||||
async (
|
||||
state: MigrationState,
|
||||
fileId: number,
|
||||
fileBuffer: ArrayBuffer,
|
||||
dataKey: CryptoKey,
|
||||
dataKeyVersion: Date,
|
||||
) => {
|
||||
state.status = "uploading";
|
||||
|
||||
const { uploadId } = await trpc().upload.startMigrationUpload.mutate({
|
||||
file: fileId,
|
||||
chunks: Math.ceil(fileBuffer.byteLength / CHUNK_SIZE),
|
||||
dekVersion: dataKeyVersion,
|
||||
});
|
||||
|
||||
await uploadBlob(uploadId, new Blob([fileBuffer]), dataKey, {
|
||||
onProgress(s) {
|
||||
state.progress = s.progress;
|
||||
state.rate = s.rate;
|
||||
},
|
||||
});
|
||||
|
||||
await trpc().upload.completeMigrationUpload.mutate({ uploadId });
|
||||
state.status = "uploaded";
|
||||
},
|
||||
{ concurrency: 1 },
|
||||
);
|
||||
|
||||
export const requestFileMigration = async (fileInfo: FileInfo) => {
|
||||
let state = states.get(fileInfo.id);
|
||||
if (state) {
|
||||
if (state.status !== "error") return;
|
||||
state.status = "queued";
|
||||
state.progress = undefined;
|
||||
state.rate = undefined;
|
||||
} else {
|
||||
state = createState("queued");
|
||||
states.set(fileInfo.id, state);
|
||||
}
|
||||
|
||||
try {
|
||||
const dataKey = fileInfo.dataKey;
|
||||
if (!dataKey) {
|
||||
throw new Error("Data key not available");
|
||||
}
|
||||
|
||||
let fileBuffer: ArrayBuffer | undefined;
|
||||
|
||||
await scheduler.schedule(
|
||||
async () => {
|
||||
state.status = "downloading";
|
||||
fileBuffer = await requestFileDownload(fileInfo.id, dataKey.key, true);
|
||||
return fileBuffer.byteLength;
|
||||
},
|
||||
() => requestFileUpload(state, fileInfo.id, fileBuffer!, dataKey.key, dataKey.version),
|
||||
);
|
||||
} catch (e) {
|
||||
state.status = "error";
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user