124_webcalibre2/src/app/dashboard/actions.ts

166 lines
4.8 KiB
TypeScript
Raw Normal View History

'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
*/
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. DELETE: Remove from OneDrive and Database
*/
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, message: "Item already removed from database" };
}
// @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);
if (accessToken) {
const rootFolder = "WebCalibre";
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) {
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.");
}
}
/**
* 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.");
}
}
/**
* 4. UPDATE & REPLACE: Full update of metadata and OneDrive content
* Uses FormData to handle the binary file upload and text fields.
*/
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;
// Handle the "root" placeholder back to null for Prisma
const parentId = parentIdRaw === "root" ? null : parentIdRaw;
let metadata = JSON.parse(metadataStr);
try {
const accessToken = await getFreshAccessToken(session.user.id);
if (!accessToken) throw new Error("Access token expired.");
// 1. OneDrive Overwrite (if file is provided)
if (newFile && newFile.size > 0) {
const rootFolder = "WebCalibre";
// PUT request to the specific file ID path overwrites content
const onedrivePath = `https://graph.microsoft.com/v1.0/me/drive/root:/${rootFolder}/${id}:/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) {
const err = await uploadRes.json();
throw new Error(`OneDrive error: ${err.error?.message}`);
}
// Update metadata with new file properties
metadata.type = newFile.name.split('.').pop()?.toUpperCase() || 'UNKNOWN';
metadata.mimeType = newFile.type;
}
// 2. Database Update
await prisma.fileNode.update({
where: { id },
data: {
name,
description,
parentId,
metadata,
// If file changed, update size; otherwise keep existing
size: newFile ? BigInt(newFile.size) : undefined,
updatedAt: new Date(),
}
});
revalidatePath("/dashboard");
return { success: true };
} catch (error: any) {
console.error("Full Update Action Failure:", error);
throw new Error(error.message || "Failed to update record.");
}
}