mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
카테고리 관련 정보도 IndexedDB에 캐싱하도록 개선
This commit is contained in:
@@ -15,16 +15,28 @@ interface FileInfo {
|
|||||||
contentType: string;
|
contentType: string;
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
lastModifiedAt: Date;
|
lastModifiedAt: Date;
|
||||||
|
categoryIds: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CategoryId = "root" | number;
|
||||||
|
|
||||||
|
interface CategoryInfo {
|
||||||
|
id: number;
|
||||||
|
parentId: CategoryId;
|
||||||
|
name: string;
|
||||||
|
files: { id: number; isRecursive: boolean }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesystem = new Dexie("filesystem") as Dexie & {
|
const filesystem = new Dexie("filesystem") as Dexie & {
|
||||||
directory: EntityTable<DirectoryInfo, "id">;
|
directory: EntityTable<DirectoryInfo, "id">;
|
||||||
file: EntityTable<FileInfo, "id">;
|
file: EntityTable<FileInfo, "id">;
|
||||||
|
category: EntityTable<CategoryInfo, "id">;
|
||||||
};
|
};
|
||||||
|
|
||||||
filesystem.version(1).stores({
|
filesystem.version(2).stores({
|
||||||
directory: "id, parentId",
|
directory: "id, parentId",
|
||||||
file: "id, parentId",
|
file: "id, parentId",
|
||||||
|
category: "id, parentId",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getDirectoryInfos = async (parentId: DirectoryId) => {
|
export const getDirectoryInfos = async (parentId: DirectoryId) => {
|
||||||
@@ -59,13 +71,29 @@ export const deleteFileInfo = async (id: number) => {
|
|||||||
await filesystem.file.delete(id);
|
await filesystem.file.delete(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCategoryInfos = async (parentId: CategoryId) => {
|
||||||
|
return await filesystem.category.where({ parentId }).toArray();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCategoryInfo = async (id: number) => {
|
||||||
|
return await filesystem.category.get(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const storeCategoryInfo = async (categoryInfo: CategoryInfo) => {
|
||||||
|
await filesystem.category.put(categoryInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteCategoryInfo = async (id: number) => {
|
||||||
|
await filesystem.category.delete(id);
|
||||||
|
};
|
||||||
|
|
||||||
export const cleanupDanglingInfos = async () => {
|
export const cleanupDanglingInfos = async () => {
|
||||||
const validDirectoryIds: number[] = [];
|
const validDirectoryIds: number[] = [];
|
||||||
const validFileIds: number[] = [];
|
const validFileIds: number[] = [];
|
||||||
const queue: DirectoryId[] = ["root"];
|
const directoryQueue: DirectoryId[] = ["root"];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const directoryId = queue.shift();
|
const directoryId = directoryQueue.shift();
|
||||||
if (!directoryId) break;
|
if (!directoryId) break;
|
||||||
|
|
||||||
const [subDirectories, files] = await Promise.all([
|
const [subDirectories, files] = await Promise.all([
|
||||||
@@ -74,13 +102,28 @@ export const cleanupDanglingInfos = async () => {
|
|||||||
]);
|
]);
|
||||||
subDirectories.forEach(({ id }) => {
|
subDirectories.forEach(({ id }) => {
|
||||||
validDirectoryIds.push(id);
|
validDirectoryIds.push(id);
|
||||||
queue.push(id);
|
directoryQueue.push(id);
|
||||||
});
|
});
|
||||||
files.forEach(({ id }) => validFileIds.push(id));
|
files.forEach(({ id }) => validFileIds.push(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validCategoryIds: number[] = [];
|
||||||
|
const categoryQueue: CategoryId[] = ["root"];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const categoryId = categoryQueue.shift();
|
||||||
|
if (!categoryId) break;
|
||||||
|
|
||||||
|
const subCategories = await filesystem.category.where({ parentId: categoryId }).toArray();
|
||||||
|
subCategories.forEach(({ id }) => {
|
||||||
|
validCategoryIds.push(id);
|
||||||
|
categoryQueue.push(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
filesystem.directory.where("id").noneOf(validDirectoryIds).delete(),
|
filesystem.directory.where("id").noneOf(validDirectoryIds).delete(),
|
||||||
filesystem.file.where("id").noneOf(validFileIds).delete(),
|
filesystem.file.where("id").noneOf(validFileIds).delete(),
|
||||||
|
filesystem.category.where("id").noneOf(validCategoryIds).delete(),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ import {
|
|||||||
getFileInfo as getFileInfoFromIndexedDB,
|
getFileInfo as getFileInfoFromIndexedDB,
|
||||||
storeFileInfo,
|
storeFileInfo,
|
||||||
deleteFileInfo,
|
deleteFileInfo,
|
||||||
|
getCategoryInfos as getCategoryInfosFromIndexedDB,
|
||||||
|
getCategoryInfo as getCategoryInfoFromIndexedDB,
|
||||||
|
storeCategoryInfo,
|
||||||
|
deleteCategoryInfo,
|
||||||
type DirectoryId,
|
type DirectoryId,
|
||||||
|
type CategoryId,
|
||||||
} from "$lib/indexedDB";
|
} from "$lib/indexedDB";
|
||||||
import { unwrapDataKey, decryptString } from "$lib/modules/crypto";
|
import { unwrapDataKey, decryptString } from "$lib/modules/crypto";
|
||||||
import type {
|
import type {
|
||||||
@@ -49,8 +54,6 @@ export interface FileInfo {
|
|||||||
categoryIds: number[];
|
categoryIds: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoryId = "root" | number;
|
|
||||||
|
|
||||||
export type CategoryInfo =
|
export type CategoryInfo =
|
||||||
| {
|
| {
|
||||||
id: "root";
|
id: "root";
|
||||||
@@ -161,7 +164,7 @@ const fetchFileInfoFromIndexedDB = async (id: number, info: Writable<FileInfo |
|
|||||||
const file = await getFileInfoFromIndexedDB(id);
|
const file = await getFileInfoFromIndexedDB(id);
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
info.set({ ...file, categoryIds: [] });
|
info.set(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
const decryptDate = async (ciphertext: string, iv: string, dataKey: CryptoKey) => {
|
const decryptDate = async (ciphertext: string, iv: string, dataKey: CryptoKey) => {
|
||||||
@@ -214,6 +217,7 @@ const fetchFileInfoFromServer = async (
|
|||||||
contentType: metadata.contentType,
|
contentType: metadata.contentType,
|
||||||
createdAt,
|
createdAt,
|
||||||
lastModifiedAt,
|
lastModifiedAt,
|
||||||
|
categoryIds: metadata.categories,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -235,6 +239,26 @@ export const getFileInfo = (fileId: number, masterKey: CryptoKey) => {
|
|||||||
return info;
|
return info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchCategoryInfoFromIndexedDB = async (
|
||||||
|
id: CategoryId,
|
||||||
|
info: Writable<CategoryInfo | null>,
|
||||||
|
) => {
|
||||||
|
if (get(info)) return;
|
||||||
|
|
||||||
|
const [category, subCategories] = await Promise.all([
|
||||||
|
id !== "root" ? getCategoryInfoFromIndexedDB(id) : undefined,
|
||||||
|
getCategoryInfosFromIndexedDB(id),
|
||||||
|
]);
|
||||||
|
const subCategoryIds = subCategories.map(({ id }) => id);
|
||||||
|
|
||||||
|
if (id === "root") {
|
||||||
|
info.set({ id, subCategoryIds });
|
||||||
|
} else {
|
||||||
|
if (!category) return;
|
||||||
|
info.set({ id, name: category.name, subCategoryIds, files: category.files });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchCategoryInfoFromServer = async (
|
const fetchCategoryInfoFromServer = async (
|
||||||
id: CategoryId,
|
id: CategoryId,
|
||||||
info: Writable<CategoryInfo | null>,
|
info: Writable<CategoryInfo | null>,
|
||||||
@@ -243,6 +267,7 @@ const fetchCategoryInfoFromServer = async (
|
|||||||
let res = await callGetApi(`/api/category/${id}`);
|
let res = await callGetApi(`/api/category/${id}`);
|
||||||
if (res.status === 404) {
|
if (res.status === 404) {
|
||||||
info.set(null);
|
info.set(null);
|
||||||
|
await deleteCategoryInfo(id as number);
|
||||||
return;
|
return;
|
||||||
} else if (!res.ok) {
|
} else if (!res.ok) {
|
||||||
throw new Error("Failed to fetch category information");
|
throw new Error("Failed to fetch category information");
|
||||||
@@ -262,6 +287,7 @@ const fetchCategoryInfoFromServer = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { files }: CategoryFileListResponse = await res.json();
|
const { files }: CategoryFileListResponse = await res.json();
|
||||||
|
const filesMapped = files.map(({ file, isRecursive }) => ({ id: file, isRecursive }));
|
||||||
|
|
||||||
info.set({
|
info.set({
|
||||||
id,
|
id,
|
||||||
@@ -269,7 +295,13 @@ const fetchCategoryInfoFromServer = async (
|
|||||||
dataKeyVersion: new Date(metadata!.dekVersion),
|
dataKeyVersion: new Date(metadata!.dekVersion),
|
||||||
name,
|
name,
|
||||||
subCategoryIds: subCategories,
|
subCategoryIds: subCategories,
|
||||||
files: files.map(({ file, isRecursive }) => ({ id: file, isRecursive })),
|
files: filesMapped,
|
||||||
|
});
|
||||||
|
await storeCategoryInfo({
|
||||||
|
id,
|
||||||
|
parentId: metadata!.parent,
|
||||||
|
name,
|
||||||
|
files: filesMapped,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -279,6 +311,7 @@ const fetchCategoryInfo = async (
|
|||||||
info: Writable<CategoryInfo | null>,
|
info: Writable<CategoryInfo | null>,
|
||||||
masterKey: CryptoKey,
|
masterKey: CryptoKey,
|
||||||
) => {
|
) => {
|
||||||
|
await fetchCategoryInfoFromIndexedDB(id, info);
|
||||||
await fetchCategoryInfoFromServer(id, info, masterKey);
|
await fetchCategoryInfoFromServer(id, info, masterKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user