feat: impl directory delete

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2025-09-14 18:12:29 +00:00
parent f32af46261
commit 59402f473f
13 changed files with 454 additions and 202 deletions

View File

@@ -1,5 +1,6 @@
import type { Doc, Id } from "@convex/_generated/dataModel"
import type { MutationCtx, QueryCtx } from "@convex/_generated/server"
import * as Err from "./error"
type Directory = {
kind: "directory"
@@ -28,6 +29,7 @@ export async function fetchContent(
ctx.db
.query("directories")
.withIndex("byParentId", (q) => q.eq("parentId", directoryId))
.filter((q) => q.eq(q.field("deletedAt"), undefined))
.collect(),
])
@@ -46,6 +48,20 @@ export async function create(
ctx: MutationCtx,
{ name, parentId }: { name: string; parentId?: Id<"directories"> },
): Promise<Id<"directories">> {
const existing = await ctx.db
.query("directories")
.withIndex("uniqueDirectoryInDirectory", (q) =>
q.eq("parentId", parentId).eq("name", name),
)
.first()
if (existing) {
throw Err.create(
Err.Code.DirectoryExists,
`Directory with name ${name} already exists in ${parentId ? `directory ${parentId}` : "root"}`,
)
}
const now = new Date().toISOString()
return await ctx.db.insert("directories", {
name,
@@ -54,3 +70,54 @@ export async function create(
updatedAt: now,
})
}
export async function moveToTrashRecursive(
ctx: MutationCtx,
directoryId: Id<"directories">,
): Promise<void> {
const now = new Date().toISOString()
const filesToDelete: Id<"files">[] = []
const directoriesToDelete: Id<"directories">[] = []
const directoryQueue: Id<"directories">[] = [directoryId]
while (directoryQueue.length > 0) {
const currentDirectoryId = directoryQueue.shift()!
directoriesToDelete.push(currentDirectoryId)
const files = await ctx.db
.query("files")
.withIndex("byDirectoryId", (q) =>
q
.eq("directoryId", currentDirectoryId)
.eq("deletedAt", undefined),
)
.collect()
for (const file of files) {
filesToDelete.push(file._id)
}
const subdirectories = await ctx.db
.query("directories")
.withIndex("byParentId", (q) =>
q.eq("parentId", currentDirectoryId).eq("deletedAt", undefined),
)
.collect()
for (const subdirectory of subdirectories) {
directoryQueue.push(subdirectory._id)
}
}
const filePatches = filesToDelete.map((fileId) =>
ctx.db.patch(fileId, { deletedAt: now }),
)
const directoryPatches = directoriesToDelete.map((dirId) =>
ctx.db.patch(dirId, { deletedAt: now }),
)
await Promise.all([...filePatches, ...directoryPatches])
}

24
convex/model/error.ts Normal file
View File

@@ -0,0 +1,24 @@
import { ConvexError } from "convex/values"
export enum Code {
DirectoryExists = "DirectoryExists",
FileExists = "FileExists",
Internal = "Internal",
}
export type ApplicationError = ConvexError<{ code: Code; message: string }>
export function isApplicationError(error: unknown): error is ApplicationError {
return (
error instanceof ConvexError &&
"code" in error.data &&
"message" in error.data
)
}
export function create(code: Code, message: string = "unknown error") {
return new ConvexError({
code,
message,
})
}

View File

@@ -1,13 +0,0 @@
import { v } from "convex/values"
import { mutation } from "../_generated/server"
export const moveToTrash = mutation({
args: {
fileId: v.id("files"),
},
handler: async (ctx, { fileId }) => {
await ctx.db.patch(fileId, {
deletedAt: new Date().toISOString(),
})
},
})