mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
126 lines
3.6 KiB
TypeScript
126 lines
3.6 KiB
TypeScript
import * as IndexedDB from "$lib/indexedDB";
|
|
import { monotonicResolve } from "$lib/utils";
|
|
import { trpc, isTRPCClientError } from "$trpc/client";
|
|
import { FilesystemCache, decryptDirectoryMetadata, decryptFileMetadata } from "./internal.svelte";
|
|
import type { MaybeDirectoryInfo } from "./types";
|
|
|
|
const cache = new FilesystemCache<DirectoryId, MaybeDirectoryInfo>();
|
|
|
|
const fetchFromIndexedDB = async (id: DirectoryId) => {
|
|
const [directory, subDirectories, files] = await Promise.all([
|
|
id !== "root" ? IndexedDB.getDirectoryInfo(id) : undefined,
|
|
IndexedDB.getDirectoryInfos(id),
|
|
IndexedDB.getFileInfos(id),
|
|
]);
|
|
|
|
if (id === "root") {
|
|
return {
|
|
id,
|
|
exists: true as const,
|
|
subDirectories,
|
|
files,
|
|
};
|
|
} else if (directory) {
|
|
return {
|
|
id,
|
|
exists: true as const,
|
|
parentId: directory.parentId,
|
|
name: directory.name,
|
|
subDirectories,
|
|
files,
|
|
};
|
|
}
|
|
};
|
|
|
|
const fetchFromServer = async (id: DirectoryId, masterKey: CryptoKey) => {
|
|
try {
|
|
const {
|
|
metadata,
|
|
subDirectories: subDirectoriesRaw,
|
|
files: filesRaw,
|
|
} = await trpc().directory.get.query({ id });
|
|
|
|
void IndexedDB.deleteDanglingDirectoryInfos(id, new Set(subDirectoriesRaw.map(({ id }) => id)));
|
|
void IndexedDB.deleteDanglingFileInfos(id, new Set(filesRaw.map(({ id }) => id)));
|
|
|
|
const existingFiles = await IndexedDB.bulkGetFileInfos(filesRaw.map((file) => file.id));
|
|
const [subDirectories, files, decryptedMetadata] = await Promise.all([
|
|
Promise.all(
|
|
subDirectoriesRaw.map(async (directory) => {
|
|
const decrypted = await decryptDirectoryMetadata(directory, masterKey);
|
|
await IndexedDB.storeDirectoryInfo({
|
|
id: directory.id,
|
|
parentId: id,
|
|
name: decrypted.name,
|
|
});
|
|
return {
|
|
id: directory.id,
|
|
...decrypted,
|
|
};
|
|
}),
|
|
),
|
|
Promise.all(
|
|
filesRaw.map(async (file, index) => {
|
|
const decrypted = await decryptFileMetadata(file, masterKey);
|
|
await IndexedDB.storeFileInfo({
|
|
id: file.id,
|
|
parentId: id,
|
|
contentType: file.contentType,
|
|
name: decrypted.name,
|
|
createdAt: decrypted.createdAt,
|
|
lastModifiedAt: decrypted.lastModifiedAt,
|
|
categoryIds: existingFiles[index]?.categoryIds ?? [],
|
|
});
|
|
return {
|
|
id: file.id,
|
|
contentType: file.contentType,
|
|
...decrypted,
|
|
};
|
|
}),
|
|
),
|
|
metadata ? decryptDirectoryMetadata(metadata, masterKey) : undefined,
|
|
]);
|
|
|
|
if (id !== "root" && metadata && decryptedMetadata) {
|
|
await IndexedDB.storeDirectoryInfo({
|
|
id,
|
|
parentId: metadata.parent,
|
|
name: decryptedMetadata.name,
|
|
});
|
|
}
|
|
|
|
if (id === "root") {
|
|
return {
|
|
id,
|
|
exists: true as const,
|
|
subDirectories,
|
|
files,
|
|
};
|
|
} else {
|
|
return {
|
|
id,
|
|
exists: true as const,
|
|
parentId: metadata!.parent,
|
|
subDirectories,
|
|
files,
|
|
...decryptedMetadata!,
|
|
};
|
|
}
|
|
} catch (e) {
|
|
if (isTRPCClientError(e) && e.data?.code === "NOT_FOUND") {
|
|
await IndexedDB.deleteDirectoryInfo(id as number);
|
|
return { id, exists: false as const };
|
|
}
|
|
throw e;
|
|
}
|
|
};
|
|
|
|
export const getDirectoryInfo = async (id: DirectoryId, masterKey: CryptoKey) => {
|
|
return await cache.get(id, (isInitial, resolve) =>
|
|
monotonicResolve(
|
|
[isInitial && fetchFromIndexedDB(id), fetchFromServer(id, masterKey)],
|
|
resolve,
|
|
),
|
|
);
|
|
};
|