Files
file-one/packages/convex/files.ts

144 lines
3.5 KiB
TypeScript
Raw Normal View History

2025-09-16 23:17:01 +00:00
import type { Id } from "@fileone/convex/_generated/dataModel"
2025-09-13 22:02:27 +01:00
import { v } from "convex/values"
2025-09-15 21:44:41 +00:00
import { authenticatedMutation, authenticatedQuery } from "./functions"
2025-09-13 22:02:27 +01:00
import type { DirectoryItem } from "./model/directories"
import * as Directories from "./model/directories"
2025-09-18 00:14:16 +00:00
import * as Files from "./model/files"
2025-09-13 22:02:27 +01:00
2025-09-15 21:44:41 +00:00
export const generateUploadUrl = authenticatedMutation({
2025-09-13 22:02:27 +01:00
handler: async (ctx) => {
2025-09-15 21:44:41 +00:00
// ctx.user and ctx.identity are automatically available
2025-09-13 22:02:27 +01:00
return await ctx.storage.generateUploadUrl()
},
})
2025-09-20 19:55:20 +00:00
export const generateFileUrl = authenticatedQuery({
args: {
storageId: v.id("_storage"),
},
handler: async (ctx, { storageId }) => {
return await ctx.storage.getUrl(storageId)
},
})
2025-09-15 21:44:41 +00:00
export const fetchFiles = authenticatedQuery({
2025-09-13 22:02:27 +01:00
args: {
directoryId: v.optional(v.id("directories")),
},
handler: async (ctx, { directoryId }) => {
return await ctx.db
.query("files")
2025-09-16 22:36:26 +00:00
.withIndex("byDirectoryId", (q) =>
q.eq("userId", ctx.user._id).eq("directoryId", directoryId),
)
2025-09-13 22:02:27 +01:00
.collect()
},
})
export const fetchRootDirectory = authenticatedQuery({
handler: async (ctx) => {
return await Directories.fetchRoot(ctx)
},
})
export const fetchDirectory = authenticatedQuery({
args: {
directoryId: v.id("directories"),
},
handler: async (ctx, { directoryId }) => {
return await Directories.fetch(ctx, { directoryId })
},
})
2025-09-15 21:44:41 +00:00
export const fetchDirectoryContent = authenticatedQuery({
2025-09-13 22:02:27 +01:00
args: {
directoryId: v.optional(v.id("directories")),
2025-09-17 00:04:12 +00:00
path: v.optional(v.string()),
2025-09-13 22:02:27 +01:00
},
2025-09-17 00:04:12 +00:00
handler: async (ctx, { directoryId, path }): Promise<DirectoryItem[]> => {
return await Directories.fetchContent(ctx, { directoryId, path })
2025-09-13 22:02:27 +01:00
},
})
2025-09-15 21:44:41 +00:00
export const createDirectory = authenticatedMutation({
2025-09-13 22:02:27 +01:00
args: {
name: v.string(),
directoryId: v.id("directories"),
2025-09-13 22:02:27 +01:00
},
handler: async (ctx, { name, directoryId }): Promise<Id<"directories">> => {
2025-09-15 21:44:41 +00:00
return await Directories.create(ctx, {
name,
parentId: directoryId,
})
2025-09-13 22:02:27 +01:00
},
})
2025-09-15 21:44:41 +00:00
export const saveFile = authenticatedMutation({
2025-09-13 22:02:27 +01:00
args: {
name: v.string(),
size: v.number(),
2025-09-19 23:17:13 +00:00
directoryId: v.id("directories"),
2025-09-13 22:02:27 +01:00
storageId: v.id("_storage"),
2025-09-14 18:49:28 +00:00
mimeType: v.optional(v.string()),
2025-09-13 22:02:27 +01:00
},
2025-09-15 21:44:41 +00:00
handler: async (ctx, { name, storageId, directoryId, size, mimeType }) => {
2025-09-13 22:02:27 +01:00
const now = new Date().toISOString()
2025-09-15 21:44:41 +00:00
2025-09-13 22:02:27 +01:00
await ctx.db.insert("files", {
name,
size,
storageId,
directoryId,
2025-09-15 21:44:41 +00:00
userId: ctx.user._id,
2025-09-14 18:49:28 +00:00
mimeType,
2025-09-13 22:02:27 +01:00
createdAt: now,
updatedAt: now,
})
},
})
2025-09-18 00:14:16 +00:00
export const renameFile = authenticatedMutation({
args: {
directoryId: v.optional(v.id("directories")),
itemId: v.id("files"),
newName: v.string(),
},
handler: async (ctx, { directoryId, itemId, newName }) => {
await Files.renameFile(ctx, { directoryId, itemId, newName })
},
})
2025-09-15 21:44:41 +00:00
export const moveToTrash = authenticatedMutation({
args: {
kind: v.union(v.literal("file"), v.literal("directory")),
itemId: v.union(v.id("files"), v.id("directories")),
},
handler: async (ctx, { itemId, kind }) => {
switch (kind) {
2025-09-15 21:44:41 +00:00
case "file": {
const file = await ctx.db.get(itemId as Id<"files">)
if (!file || file.userId !== ctx.user._id) {
throw new Error("File not found or access denied")
}
await ctx.db.patch(itemId, {
deletedAt: new Date().toISOString(),
})
break
2025-09-15 21:44:41 +00:00
}
case "directory": {
const directory = await ctx.db.get(itemId as Id<"directories">)
if (!directory || directory.userId !== ctx.user._id) {
throw new Error("Directory not found or access denied")
}
await Directories.moveToTrashRecursive(
ctx,
itemId as Id<"directories">,
)
break
2025-09-15 21:44:41 +00:00
}
}
return itemId
},
})