'use server'; import { auth } from "@/auth"; import { prisma } from "@/lib/prisma"; import { revalidatePath } from "next/cache"; import { getFreshAccessToken } from "@/lib/auth-utils"; /** * 1. FETCH: Get all file nodes for the Dashboard */ export async function getFileNodes() { try { const nodes = await prisma.fileNode.findMany({ orderBy: { updatedAt: 'desc', }, }); return nodes; } catch (error) { console.error("Error fetching file nodes:", error); return []; } } /** * 2. DOWNLOAD: Generates the authenticated OneDrive URL */ export async function getDownloadUrlAction(id: string) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); const file = await prisma.fileNode.findUnique({ where: { id } }); if (!file || !file.oneDriveId) throw new Error("File not found or missing cloud ID"); const accessToken = await getFreshAccessToken(session.user.id); const res = await fetch( `https://graph.microsoft.com/v1.0/me/drive/items/${file.oneDriveId}`, { headers: { Authorization: `Bearer ${accessToken}` } } ); if (!res.ok) throw new Error("Failed to contact OneDrive"); const data = await res.json(); const downloadUrl = data["@microsoft.graph.downloadUrl"]; if (!downloadUrl) throw new Error("OneDrive did not provide a download link"); return { downloadUrl }; } /** * 3. DELETE: Remove from OneDrive (via ID) and Database * Folders are virtual (DB only), so cloud deletion is skipped if oneDriveId is null. */ export async function deleteFileAction(fileId: string) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); const node = await prisma.fileNode.findUnique({ where: { id: fileId }, }); if (!node) { revalidatePath("/dashboard"); return { success: true }; } // @ts-ignore const isAdmin = session.user.role === "ADMIN"; const isOwner = node.ownerId === session.user.id; if (!isAdmin && !isOwner) { throw new Error("Permission Denied."); } try { const accessToken = await getFreshAccessToken(session.user.id); // Only attempt cloud deletion if it's a file/storage with a oneDriveId. // Virtual folders created in the DB have no oneDriveId and are skipped. if (accessToken && node.oneDriveId) { const onedriveRes = await fetch( `https://graph.microsoft.com/v1.0/me/drive/items/${node.oneDriveId}`, { method: "DELETE", headers: { Authorization: `Bearer ${accessToken}` }, } ); if (!onedriveRes.ok && onedriveRes.status !== 404) { console.warn("OneDrive Deletion Warning: Cloud record might still exist."); } } } catch (cloudError) { console.error("Cloud cleanup failed:", cloudError); } try { await prisma.fileNode.delete({ where: { id: fileId } }); revalidatePath("/dashboard"); revalidatePath("/upload"); return { success: true }; } catch (dbError) { throw new Error("Failed to remove the record from the database."); } } /** * 4. MOVE: Assign file to folder or folder to another folder (Virtual Move) */ export async function moveNodeAction(nodeId: string, newParentId: string | null) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); if (nodeId === newParentId) throw new Error("Cannot move to self."); try { await prisma.fileNode.update({ where: { id: nodeId }, data: { parentId: newParentId } }); revalidatePath("/dashboard"); return { success: true }; } catch (error) { throw new Error("Move failed."); } } /** * 5. UPDATE & REPLACE: Full update of metadata and OneDrive content */ export async function updateFileFullAction(formData: FormData) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); const id = formData.get("id") as string; const name = formData.get("name") as string; const description = formData.get("description") as string; const parentIdRaw = formData.get("parentId") as string; const metadataStr = formData.get("metadata") as string; const newFile = formData.get("file") as File | null; const parentId = parentIdRaw === "root" ? null : parentIdRaw; let metadata = JSON.parse(metadataStr); try { const accessToken = await getFreshAccessToken(session.user.id); const node = await prisma.fileNode.findUnique({ where: { id } }); // Update physical file content only if a new file is uploaded and we have a target oneDriveId if (newFile && newFile.size > 0 && node?.oneDriveId) { const onedrivePath = `https://graph.microsoft.com/v1.0/me/drive/items/${node.oneDriveId}/content`; const uploadRes = await fetch(onedrivePath, { method: "PUT", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": newFile.type }, body: Buffer.from(await newFile.arrayBuffer()), }); if (!uploadRes.ok) throw new Error("OneDrive content update failed"); metadata.type = newFile.name.split('.').pop()?.toUpperCase() || 'UNKNOWN'; metadata.mimeType = newFile.type; } await prisma.fileNode.update({ where: { id }, data: { name, description, parentId, metadata, size: newFile ? BigInt(newFile.size) : undefined, updatedAt: new Date(), } }); revalidatePath("/dashboard"); return { success: true }; } catch (error: any) { console.error("Full Update Failure:", error); throw new Error(error.message || "Failed to update record."); } }