124_webcalibre2/src/app/upload/_actions.ts

105 lines
3.5 KiB
TypeScript
Raw Normal View History

'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.
* No physical folder is created on OneDrive to keep storage flat and fast.
*/
export async function createFolderAction(name: string, parentId: string | null = null) {
const session = await auth();
if (!session?.user?.id) throw new Error("Unauthorized");
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,
}
});
revalidatePath("/upload");
revalidatePath("/dashboard");
return { success: true };
}
/**
* Uploads a file to a unique UUID folder on OneDrive
* and links it to a virtual parent 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");
const accessToken = await getFreshAccessToken(session.user.id);
const rootFolder = "WebCalibre";
const internalId = crypto.randomUUID();
// 1. Ensure WebCalibre exists (Simplified for brevity)
// ... (Keep the root folder check logic from your previous version)
// 2. Create the unique UUID folder on OneDrive
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) throw new Error("Storage directory creation failed");
const subFolderData = await createSubFolderRes.json();
// 3. Create Upload Session & Upload
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());
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 DB
await prisma.fileNode.create({
data: {
id: internalId,
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 HIERARCHY
metadata: {
type: file.name.split('.').pop()?.toUpperCase() || "UNKNOWN",
mimeType: file.type
}
}
});
revalidatePath("/dashboard");
return { success: true };
}