// src/app/dashboard/actions.ts 'use server'; import { auth } from "@/auth"; import { revalidatePath } from "next/cache"; import { getAllFileNodes, getFileNodeById, updateFileNode, deleteFileNode } from "@/data-access/file-nodes"; import { getOneDriveItem, deleteFromOneDrive, uploadToOneDrive } from "@/services/onedrive"; import { getEnrichedMetadataFromCloud } from "@/data-access/file-nodes"; import { getFreshAccessToken } from "@/lib/auth-utils"; import { getOneDriveFileBuffer } from "@/services/onedrive"; // Import your working service import { extractMetadata } from "@/lib/metadata-extractor"; /** * 1. FETCH: Get all file nodes * Now simply calls the DAL. Error handling is left to the caller (the UI). */ export async function getFileNodes() { return await getAllFileNodes(); } /** * 2. DOWNLOAD: Generates the authenticated OneDrive URL * Orchestrates the session check, DAL lookup, and Service call. */ export async function getDownloadUrlAction(id: string) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); const file = await getFileNodeById(id); if (!file || !file.oneDriveId) throw new Error("File not found or missing cloud ID"); // Service handles token refresh and graph request internally const data = await getOneDriveItem(session.user.id, file.oneDriveId); const downloadUrl = data["@microsoft.graph.downloadUrl"]; if (!downloadUrl) throw new Error("OneDrive did not provide a download URL"); return downloadUrl; } /** * 3. DELETE: Removes from both Cloud and Database */ export async function deleteFileNodeAction(id: string) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); try { const file = await getFileNodeById(id); if (!file) throw new Error("File record not found"); // Phase 1: Cloud Deletion if (file.oneDriveId) { await deleteFromOneDrive(session.user.id, file.oneDriveId); } // Phase 2: Database Deletion await deleteFileNode(id); revalidatePath("/dashboard"); return { success: true }; } catch (error) { console.error("Delete Error:", error); return { success: false, error: "Failed to delete file" }; } } /** * 4. UPDATE: Modify record and optionally sync new content to OneDrive */ export async function updateFileNodeAction(id: string, formData: FormData) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); 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 node = await getFileNodeById(id); // If a new file is uploaded, push it to OneDrive first if (newFile && newFile.size > 0 && node?.oneDriveId) { await uploadToOneDrive(session.user.id, newFile, node.oneDriveId); metadata.type = newFile.name.split('.').pop()?.toUpperCase() || 'UNKNOWN'; metadata.mimeType = newFile.type; } // Update the database via DAL await updateFileNode(id, { name, description, parentId, metadata, size: newFile ? BigInt(newFile.size) : undefined, }); revalidatePath("/dashboard"); return { success: true }; } catch (error) { console.error("Update Error:", error); return { success: false, error: "Failed to update record" }; } } // export async function getMetadataPreviewAction(fileId: string) { // const session = await auth(); // if (!session?.user?.id) throw new Error("Unauthorized"); // try { // console.log(`🔍 Starting enhancement for file: ${fileId}`); // // This calls the DAL -> which calls the Service -> which calls OneDrive // const data = await getEnrichedMetadataFromCloud(fileId); // // This log will show you exactly what we found in your terminal! // console.log("✅ Extracted Metadata Result:", data); // return { success: true, data }; // } catch (error: any) { // console.error("❌ Enhancement Action Error:", error.message); // return { success: false, error: error.message }; // } // } /** * 5. ENHANCE (Magic Fill): Extracts deep metadata from the actual file binary */ // export async function getMetadataPreviewAction(fileId: string) { // const session = await auth(); // if (!session?.user?.id) throw new Error("Unauthorized"); // console.log(`\n--- 🔍 Magic Fill Started for File ID: ${fileId} ---`); // try { // // 1. Get the record from DB to get the oneDriveId and Name // const node = await getFileNodeById(fileId); // if (!node || !node.oneDriveId) { // throw new Error("File not found or not synced with OneDrive"); // } // // 2. Get fresh token // const token = await getFreshAccessToken(session.user.id); // // 3. Fetch the actual binary content from Microsoft Graph // console.log(`📡 Fetching binary from Microsoft Graph...`); // let response; // try { // response = await fetch( // `https://graph.microsoftonline.com/v1.0/me/drive/items/${node.oneDriveId}/content`, // { // headers: { Authorization: `Bearer ${token}` }, // cache: 'no-store' // Ensure we aren't hitting a stale server cache // } // ); // } catch (err: any) { // console.error("❌ THE ACTUAL NETWORK ERROR:"); // console.error("Message:", err.message); // console.error("Cause/Stack:", err.cause || err.stack); // This is the gold mine // throw new Error(`Server-side fetch failed: ${err.message}`); // } // if (!response.ok) { // throw new Error(`Failed to fetch file content: ${response.statusText}`); // } // const arrayBuffer = await response.arrayBuffer(); // const buffer = Buffer.from(arrayBuffer); // console.log(`📦 Downloaded ${buffer.length} bytes.`); // // 4. Run the Metadata Utility // const extractedData = await extractMetadata(buffer, node.name); // // --- 🏁 THE TERMINAL LOG YOU REQUESTED --- // console.log("✅ RAW DATA EXTRACTED FROM FILE:"); // console.dir(extractedData, { depth: null, colors: true }); // console.log(`--- 🏁 Magic Fill Finished ---\n`); // return { success: true, data: extractedData }; // } catch (error: any) { // console.error("❌ Magic Fill Error:", error.message); // return { success: false, error: error.message }; // } // } export async function getMetadataPreviewAction(fileId: string) { const session = await auth(); if (!session?.user?.id) throw new Error("Unauthorized"); console.log(`\n--- 🔍 Magic Fill Started for File ID: ${fileId} ---`); try { const node = await getFileNodeById(fileId); if (!node || !node.oneDriveId) throw new Error("File not found"); // 1. Get the token from our utility const token = await getFreshAccessToken(session.user.id); // LOG: Just check the length to be sure it's not empty console.log(`🔑 Token retrieved (Length: ${token.length})`); console.log(`📡 Fetching binary for: ${node.name}...`); // 2. Fetch the content from Microsoft Graph const response = await fetch( `https://graph.microsoft.com/v1.0/me/drive/items/${node.oneDriveId}/content`, { headers: { 'Authorization': `Bearer ${token}`, 'Accept': '*/*' }, } ); if (!response.ok) { // If it fails here, we'll see the real reason from Microsoft const errorText = await response.text(); console.error("❌ Microsoft Graph Error Response:", errorText); throw new Error(`OneDrive Download Failed: ${response.status}`); } const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); console.log(`📦 Success! Downloaded ${buffer.length} bytes.`); // 3. Extract Metadata const extractedData = await extractMetadata(buffer, node.name); console.log("✅ RAW DATA EXTRACTED:"); console.dir(extractedData, { depth: null, colors: true }); return { success: true, data: extractedData }; } catch (error: any) { console.error("❌ Magic Fill Error:", error.message); return { success: false, error: error.message }; } }