mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-14 22:08:45 +00:00
디렉터리를 삭제하는 경우, 디렉터리 하위에 있던 파일의 캐시를 자동으로 삭제하도록 구현
This commit is contained in:
@@ -129,14 +129,15 @@ export const unregisterDirectory = async (userId: number, directoryId: number) =
|
|||||||
return await db.transaction(
|
return await db.transaction(
|
||||||
async (tx) => {
|
async (tx) => {
|
||||||
const unregisterFiles = async (parentId: number) => {
|
const unregisterFiles = async (parentId: number) => {
|
||||||
const files = await tx
|
return await tx
|
||||||
.delete(file)
|
.delete(file)
|
||||||
.where(and(eq(file.userId, userId), eq(file.parentId, parentId)))
|
.where(and(eq(file.userId, userId), eq(file.parentId, parentId)))
|
||||||
.returning({ path: file.path });
|
.returning({ id: file.id, path: file.path });
|
||||||
return files.map(({ path }) => path);
|
|
||||||
};
|
};
|
||||||
const unregisterDirectoryRecursively = async (directoryId: number): Promise<string[]> => {
|
const unregisterDirectoryRecursively = async (
|
||||||
const filePaths = await unregisterFiles(directoryId);
|
directoryId: number,
|
||||||
|
): Promise<{ id: number; path: string }[]> => {
|
||||||
|
const files = await unregisterFiles(directoryId);
|
||||||
const subDirectories = await tx
|
const subDirectories = await tx
|
||||||
.select({ id: directory.id })
|
.select({ id: directory.id })
|
||||||
.from(directory)
|
.from(directory)
|
||||||
@@ -149,7 +150,7 @@ export const unregisterDirectory = async (userId: number, directoryId: number) =
|
|||||||
if (deleteRes.changes === 0) {
|
if (deleteRes.changes === 0) {
|
||||||
throw new IntegrityError("Directory not found");
|
throw new IntegrityError("Directory not found");
|
||||||
}
|
}
|
||||||
return filePaths.concat(...subDirectoryFilePaths);
|
return files.concat(...subDirectoryFilePaths);
|
||||||
};
|
};
|
||||||
return await unregisterDirectoryRecursively(directoryId);
|
return await unregisterDirectoryRecursively(directoryId);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ export const directoryInfoResponse = z.object({
|
|||||||
});
|
});
|
||||||
export type DirectoryInfoResponse = z.infer<typeof directoryInfoResponse>;
|
export type DirectoryInfoResponse = z.infer<typeof directoryInfoResponse>;
|
||||||
|
|
||||||
|
export const directoryDeleteResponse = z.object({
|
||||||
|
deletedFiles: z.number().int().positive().array(),
|
||||||
|
});
|
||||||
|
export type DirectoryDeleteResponse = z.infer<typeof directoryDeleteResponse>;
|
||||||
|
|
||||||
export const directoryRenameRequest = z.object({
|
export const directoryRenameRequest = z.object({
|
||||||
dekVersion: z.string().datetime(),
|
dekVersion: z.string().datetime(),
|
||||||
name: z.string().base64().nonempty(),
|
name: z.string().base64().nonempty(),
|
||||||
|
|||||||
@@ -34,8 +34,13 @@ export const getDirectoryInformation = async (userId: number, directoryId: "root
|
|||||||
|
|
||||||
export const deleteDirectory = async (userId: number, directoryId: number) => {
|
export const deleteDirectory = async (userId: number, directoryId: number) => {
|
||||||
try {
|
try {
|
||||||
const filePaths = await unregisterDirectory(userId, directoryId);
|
const files = await unregisterDirectory(userId, directoryId);
|
||||||
filePaths.map((path) => unlink(path)); // Intended
|
return {
|
||||||
|
files: files.map(({ id, path }) => {
|
||||||
|
unlink(path); // Intended
|
||||||
|
return id;
|
||||||
|
}),
|
||||||
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof IntegrityError && e.message === "Directory not found") {
|
if (e instanceof IntegrityError && e.message === "Directory not found") {
|
||||||
error(404, "Invalid directory id");
|
error(404, "Invalid directory id");
|
||||||
|
|||||||
@@ -29,7 +29,12 @@
|
|||||||
fileInfo: getFileInfo(index.fileId, $masterKeyStore?.get(1)?.key!),
|
fileInfo: getFileInfo(index.fileId, $masterKeyStore?.get(1)?.key!),
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.index.lastRetrievedAt.getTime() - b.index.lastRetrievedAt.getTime());
|
.sort((a, b) => a.index.lastRetrievedAt.getTime() - b.index.lastRetrievedAt.getTime());
|
||||||
fileCacheTotalSize = fileCache.reduce((acc, { index }) => acc + index.size, 0);
|
});
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (fileCache) {
|
||||||
|
fileCacheTotalSize = fileCache.reduce((acc, { index }) => acc + index.size, 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@
|
|||||||
|
|
||||||
<div class="flex h-full flex-col">
|
<div class="flex h-full flex-col">
|
||||||
<TopBar title="캐시" />
|
<TopBar title="캐시" />
|
||||||
{#if fileCache}
|
{#if fileCache && fileCache.length > 0}
|
||||||
<div class="space-y-4 pb-4">
|
<div class="space-y-4 pb-4">
|
||||||
<div class="space-y-1 break-keep text-gray-800">
|
<div class="space-y-1 break-keep text-gray-800">
|
||||||
<p>
|
<p>
|
||||||
@@ -56,7 +61,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-grow items-center justify-center">
|
<div class="flex flex-grow items-center justify-center">
|
||||||
<p class="text-gray-500">캐시 목록을 불러오고 있어요.</p>
|
<p class="text-gray-500">
|
||||||
|
{#if fileCache}
|
||||||
|
캐시된 파일이 없어요.
|
||||||
|
{:else}
|
||||||
|
캐시 목록을 불러오고 있어요.
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import type {
|
|||||||
HmacSecretListResponse,
|
HmacSecretListResponse,
|
||||||
DuplicateFileScanRequest,
|
DuplicateFileScanRequest,
|
||||||
DuplicateFileScanResponse,
|
DuplicateFileScanResponse,
|
||||||
|
DirectoryDeleteResponse,
|
||||||
} from "$lib/server/schemas";
|
} from "$lib/server/schemas";
|
||||||
import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores";
|
import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores";
|
||||||
|
|
||||||
@@ -191,6 +192,15 @@ export const requestDirectoryEntryRename = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const requestDirectoryEntryDeletion = async (entry: SelectedDirectoryEntry) => {
|
export const requestDirectoryEntryDeletion = async (entry: SelectedDirectoryEntry) => {
|
||||||
await callPostApi(`/api/${entry.type}/${entry.id}/delete`);
|
const res = await callPostApi(`/api/${entry.type}/${entry.id}/delete`);
|
||||||
await deleteFileCache(entry.id);
|
if (!res.ok) return false;
|
||||||
|
|
||||||
|
if (entry.type === "directory") {
|
||||||
|
const { deletedFiles }: DirectoryDeleteResponse = await res.json();
|
||||||
|
await Promise.all(deletedFiles.map(deleteFileCache));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
await deleteFileCache(entry.id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { error, text } from "@sveltejs/kit";
|
import { error, json } from "@sveltejs/kit";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { authorize } from "$lib/server/modules/auth";
|
import { authorize } from "$lib/server/modules/auth";
|
||||||
|
import { directoryDeleteResponse, type DirectoryDeleteResponse } from "$lib/server/schemas";
|
||||||
import { deleteDirectory } from "$lib/server/services/directory";
|
import { deleteDirectory } from "$lib/server/services/directory";
|
||||||
import type { RequestHandler } from "./$types";
|
import type { RequestHandler } from "./$types";
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ export const POST: RequestHandler = async ({ locals, params }) => {
|
|||||||
if (!zodRes.success) error(400, "Invalid path parameters");
|
if (!zodRes.success) error(400, "Invalid path parameters");
|
||||||
const { id } = zodRes.data;
|
const { id } = zodRes.data;
|
||||||
|
|
||||||
await deleteDirectory(userId, id);
|
const { files } = await deleteDirectory(userId, id);
|
||||||
return text("Directory deleted", { headers: { "Content-Type": "text/plain" } });
|
return json(
|
||||||
|
directoryDeleteResponse.parse({ deletedFiles: files } satisfies DirectoryDeleteResponse),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user