2026-01-08 05:41:31 +00:00
|
|
|
'use server';
|
2026-01-11 13:41:54 +00:00
|
|
|
|
2026-01-08 05:41:31 +00:00
|
|
|
import { auth } from "@/auth";
|
2026-01-11 13:41:54 +00:00
|
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
|
|
|
import { getFreshAccessToken } from "@/lib/auth-utils";
|
2026-01-08 05:41:31 +00:00
|
|
|
|
2026-01-11 13:41:54 +00:00
|
|
|
/**
|
|
|
|
|
* 1. FETCH: Get all file nodes
|
|
|
|
|
*/
|
2026-01-08 05:41:31 +00:00
|
|
|
export async function getFileNodes() {
|
2026-01-11 13:41:54 +00:00
|
|
|
try {
|
|
|
|
|
const nodes = await prisma.fileNode.findMany({
|
|
|
|
|
orderBy: {
|
|
|
|
|
updatedAt: 'desc',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return nodes;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error fetching file nodes:", error);
|
|
|
|
|
return [];
|
2026-01-08 05:41:31 +00:00
|
|
|
}
|
2026-01-11 13:41:54 +00:00
|
|
|
}
|
2026-01-08 05:41:31 +00:00
|
|
|
|
2026-01-11 13:41:54 +00:00
|
|
|
/**
|
|
|
|
|
* 2. DELETE: Remove from OneDrive and Database
|
|
|
|
|
* Improved to ensure DB removal even if OneDrive API returns errors.
|
|
|
|
|
*/
|
|
|
|
|
export async function deleteFileAction(fileId: string) {
|
|
|
|
|
const session = await auth();
|
|
|
|
|
if (!session?.user?.id) throw new Error("Unauthorized");
|
|
|
|
|
|
|
|
|
|
// 1. Find the node to check permissions
|
|
|
|
|
const node = await prisma.fileNode.findUnique({
|
|
|
|
|
where: { id: fileId },
|
2026-01-08 05:41:31 +00:00
|
|
|
});
|
|
|
|
|
|
2026-01-11 13:41:54 +00:00
|
|
|
if (!node) {
|
|
|
|
|
// If not in DB, trigger a refresh anyway to clear the UI
|
|
|
|
|
revalidatePath("/dashboard");
|
|
|
|
|
return { success: true, message: "Item already removed from database" };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @ts-ignore - role added via auth callbacks
|
|
|
|
|
const isAdmin = session.user.role === "ADMIN";
|
|
|
|
|
const isOwner = node.ownerId === session.user.id;
|
|
|
|
|
|
|
|
|
|
if (!isAdmin && !isOwner) {
|
|
|
|
|
throw new Error("Permission Denied: You do not have authority to delete this item.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.isFolder && !isAdmin) {
|
|
|
|
|
throw new Error("Security Restriction: Only Administrators can delete folders.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. OneDrive Deletion Attempt
|
|
|
|
|
try {
|
|
|
|
|
const accessToken = await getFreshAccessToken(session.user.id);
|
|
|
|
|
|
|
|
|
|
if (accessToken) {
|
|
|
|
|
const rootFolder = "WebCalibre";
|
|
|
|
|
// We target the folder on OneDrive by the ID stored in our DB
|
|
|
|
|
const onedrivePath = `https://graph.microsoft.com/v1.0/me/drive/root:/${rootFolder}/${node.id}`;
|
|
|
|
|
|
|
|
|
|
const onedriveRes = await fetch(onedrivePath, {
|
|
|
|
|
method: "DELETE",
|
|
|
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!onedriveRes.ok && onedriveRes.status !== 404) {
|
|
|
|
|
const errData = await onedriveRes.json().catch(() => ({}));
|
|
|
|
|
console.warn("OneDrive Deletion Warning (Cloud record might still exist):", errData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (cloudError) {
|
|
|
|
|
// We catch cloud errors but DO NOT throw them, so we can proceed to delete the DB record
|
|
|
|
|
console.error("Cloud communication failed, proceeding with DB cleanup:", cloudError);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Database Deletion (The Source of Truth for your UI)
|
|
|
|
|
try {
|
|
|
|
|
await prisma.fileNode.delete({
|
|
|
|
|
where: { id: fileId }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
revalidatePath("/dashboard");
|
|
|
|
|
revalidatePath("/upload");
|
|
|
|
|
|
|
|
|
|
return { success: true };
|
|
|
|
|
} catch (dbError: any) {
|
|
|
|
|
console.error("Database deletion failed:", dbError);
|
|
|
|
|
throw new Error("Failed to remove the record from the database.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 3. MOVE: Assign file to folder or folder to another folder
|
|
|
|
|
*/
|
|
|
|
|
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.");
|
|
|
|
|
}
|
2026-01-08 05:41:31 +00:00
|
|
|
}
|