'use server'; import { auth } from "@/auth"; import { getFreshAccessToken } from "@/lib/auth-utils"; import { prisma } from "@/lib/prisma"; import { revalidatePath } from "next/cache"; /** * Creates a virtual folder in the database. * We explicitly set the metadata type to "FOLDER" so the Dashboard icon * and Location logic work immediately. */ export async function createFolderAction(name: string, parentId: string | null = null) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); // We generate a manual UUID because the schema requires 'id' but has no default generator const internalId = crypto.randomUUID(); await prisma.fileNode.create({ data: { id: internalId, name: name, isFolder: true, path: `/virtual/${name}`, ownerId: session.user.id, parentId: parentId || null, metadata: { type: "FOLDER", mimeType: "inode/directory" } } }); revalidatePath("/upload"); revalidatePath("/dashboard"); return { success: true }; } /** * Uploads a file to OneDrive into a unique UUID folder * and links it to a virtual parent (Project/Folder) in the DB. */ export async function uploadFileAction(formData: FormData) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); const file = formData.get("file") as File; const description = formData.get("description") as string || ""; const parentId = formData.get("parentId") as string | null; if (!file) throw new Error("No file selected"); // Use the utility to ensure we have a valid JWT (fixing the "no dots" error) const accessToken = await getFreshAccessToken(session.user.id); const rootFolder = "WebCalibre"; const internalId = crypto.randomUUID(); // 1. Create the unique UUID folder on OneDrive inside WebCalibre const createSubFolderRes = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:/${rootFolder}:/children`, { method: "POST", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: internalId, folder: {}, "@microsoft.graph.conflictBehavior": "fail" }) }); if (!createSubFolderRes.ok) { const err = await createSubFolderRes.json(); console.error("OneDrive Folder Creation Error:", err); throw new Error("Storage directory creation failed"); } const subFolderData = await createSubFolderRes.json(); // 2. Create Upload Session for the file const sessionUrl = `https://graph.microsoft.com/v1.0/me/drive/items/${subFolderData.id}:/${encodeURIComponent(file.name)}:/createUploadSession`; const sessionRes = await fetch(sessionUrl, { method: "POST", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ item: { "@microsoft.graph.conflictBehavior": "replace" } }) }); const { uploadUrl } = await sessionRes.json(); const buffer = Buffer.from(await file.arrayBuffer()); // 3. Perform the actual upload const uploadRes = await fetch(uploadUrl, { method: "PUT", headers: { "Content-Length": `${file.size}`, "Content-Range": `bytes 0-${file.size - 1}/${file.size}` }, body: buffer }); if (!uploadRes.ok) throw new Error("OneDrive stream failed"); const driveItem = await uploadRes.json(); // 4. Record in Database with full metadata and virtual hierarchy const extension = file.name.split('.').pop()?.toUpperCase() || "UNKNOWN"; await prisma.fileNode.create({ data: { id: internalId, // Matches the folder name on OneDrive oneDriveId: driveItem.id, name: file.name, description: description, size: BigInt(file.size), isFolder: false, path: `/${rootFolder}/${internalId}/${file.name}`, ownerId: session.user.id, parentId: parentId || null, // Virtual link to the Project folder metadata: { type: extension, mimeType: file.type } } }); revalidatePath("/dashboard"); return { success: true }; }