카테고리 관련 정보도 IndexedDB에 캐싱하도록 개선

This commit is contained in:
static
2025-01-22 22:49:07 +09:00
parent f34764ffe0
commit dd0a887576
2 changed files with 84 additions and 8 deletions

View File

@@ -15,16 +15,28 @@ interface FileInfo {
contentType: string;
createdAt?: 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 & {
directory: EntityTable<DirectoryInfo, "id">;
file: EntityTable<FileInfo, "id">;
category: EntityTable<CategoryInfo, "id">;
};
filesystem.version(1).stores({
filesystem.version(2).stores({
directory: "id, parentId",
file: "id, parentId",
category: "id, parentId",
});
export const getDirectoryInfos = async (parentId: DirectoryId) => {
@@ -59,13 +71,29 @@ export const deleteFileInfo = async (id: number) => {
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 () => {
const validDirectoryIds: number[] = [];
const validFileIds: number[] = [];
const queue: DirectoryId[] = ["root"];
const directoryQueue: DirectoryId[] = ["root"];
while (true) {
const directoryId = queue.shift();
const directoryId = directoryQueue.shift();
if (!directoryId) break;
const [subDirectories, files] = await Promise.all([
@@ -74,13 +102,28 @@ export const cleanupDanglingInfos = async () => {
]);
subDirectories.forEach(({ id }) => {
validDirectoryIds.push(id);
queue.push(id);
directoryQueue.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([
filesystem.directory.where("id").noneOf(validDirectoryIds).delete(),
filesystem.file.where("id").noneOf(validFileIds).delete(),
filesystem.category.where("id").noneOf(validCategoryIds).delete(),
]);
};

View File

@@ -9,7 +9,12 @@ import {
getFileInfo as getFileInfoFromIndexedDB,
storeFileInfo,
deleteFileInfo,
getCategoryInfos as getCategoryInfosFromIndexedDB,
getCategoryInfo as getCategoryInfoFromIndexedDB,
storeCategoryInfo,
deleteCategoryInfo,
type DirectoryId,
type CategoryId,
} from "$lib/indexedDB";
import { unwrapDataKey, decryptString } from "$lib/modules/crypto";
import type {
@@ -49,8 +54,6 @@ export interface FileInfo {
categoryIds: number[];
}
type CategoryId = "root" | number;
export type CategoryInfo =
| {
id: "root";
@@ -161,7 +164,7 @@ const fetchFileInfoFromIndexedDB = async (id: number, info: Writable<FileInfo |
const file = await getFileInfoFromIndexedDB(id);
if (!file) return;
info.set({ ...file, categoryIds: [] });
info.set(file);
};
const decryptDate = async (ciphertext: string, iv: string, dataKey: CryptoKey) => {
@@ -214,6 +217,7 @@ const fetchFileInfoFromServer = async (
contentType: metadata.contentType,
createdAt,
lastModifiedAt,
categoryIds: metadata.categories,
});
};
@@ -235,6 +239,26 @@ export const getFileInfo = (fileId: number, masterKey: CryptoKey) => {
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 (
id: CategoryId,
info: Writable<CategoryInfo | null>,
@@ -243,6 +267,7 @@ const fetchCategoryInfoFromServer = async (
let res = await callGetApi(`/api/category/${id}`);
if (res.status === 404) {
info.set(null);
await deleteCategoryInfo(id as number);
return;
} else if (!res.ok) {
throw new Error("Failed to fetch category information");
@@ -262,6 +287,7 @@ const fetchCategoryInfoFromServer = async (
}
const { files }: CategoryFileListResponse = await res.json();
const filesMapped = files.map(({ file, isRecursive }) => ({ id: file, isRecursive }));
info.set({
id,
@@ -269,7 +295,13 @@ const fetchCategoryInfoFromServer = async (
dataKeyVersion: new Date(metadata!.dekVersion),
name,
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>,
masterKey: CryptoKey,
) => {
await fetchCategoryInfoFromIndexedDB(id, info);
await fetchCategoryInfoFromServer(id, info, masterKey);
};