// src/data-access/file-nodes.ts import "server-only"; import { getOneDriveFileBuffer } from "@/services/onedrive"; import { extractMetadata } from "@/lib/metadata-extractor"; import { prisma } from "@/lib/prisma"; /** * FETCH: Retrieve all nodes for the dashboard. * Centralizing this here allows us to change sort order or filters * in one place for the entire application. */ export async function getAllFileNodes() { return await prisma.fileNode.findMany({ orderBy: { updatedAt: 'desc', }, }); } /** * FETCH: Get a single node by ID. * Used by the Download route and Update pages to verify a file exists. */ export async function getFileNodeById(id: string) { return await prisma.fileNode.findUnique({ where: { id }, }); } /** * UPDATE: Modify metadata, name, or virtual location. * This function accepts the data object to keep the DAL flexible. */ export async function updateFileNode(id: string, data: any) { return await prisma.fileNode.update({ where: { id }, data: { ...data, updatedAt: new Date(), }, }); } /** * DELETE: Remove the record from the database. * Cloud deletion should be handled by the Service Layer before calling this. */ export async function deleteFileNode(id: string) { return await prisma.fileNode.delete({ where: { id }, }); } /** * MASTER CREATE: Handles both standard uploads and virtual folders. * If no ID is provided, it generates a fresh UUID. */ export async function createFileNode(data: { id?: string; // Optional: used for virtual folders/UUID storage oneDriveId: string | null; name: string; hash?: string | null; // ✅ ADDED: For SHA-256 duplicate prevention description?: string; isFolder: boolean; path: string; ownerId: string; parentId?: string | null; // Optional: for nested structures size?: bigint; metadata: any; }) { return await prisma.fileNode.create({ data: { ...data, id: data.id ?? crypto.randomUUID(), // Use provided ID or generate new one } }); } // ... other functions (getAllFileNodes, etc) /** * UPSERT: Create or Update a file node based on OneDrive ID * Moved here because it interacts with the Database. */ export async function upsertFileNode(oneDriveId: string, data: { name: string; size: bigint; isFolder: boolean; path: string; ownerId: string; metadata: any; hash?: string | null; // ✅ ADDED: Keep hash in sync during upserts }) { return await prisma.fileNode.upsert({ where: { oneDriveId }, update: { name: data.name, size: data.size, isFolder: data.isFolder, path: data.path, hash: data.hash, // ✅ ADDED updatedAt: new Date(), }, create: { id: crypto.randomUUID(), oneDriveId: oneDriveId, name: data.name, size: data.size, isFolder: data.isFolder, path: data.path, ownerId: data.ownerId, metadata: data.metadata, hash: data.hash, // ✅ ADDED } }); } /** * FETCH: Get all virtual folders for selection in dropdowns. * Used by the BulkUpload page to set destinations. */ export async function getAllFolders() { return await prisma.fileNode.findMany({ where: { isFolder: true }, select: { id: true, name: true }, orderBy: { name: 'asc' } }); } /** * UPSERT BY HASH: The core of the Disaster Recovery process. * If a hash exists, we update the OneDrive ID (Restoring the link). * If not, we create a new entry. */ export async function upsertFileNodeByHash(data: { name: string; hash: string; oneDriveId: string; parentId?: string | null; size: bigint; ownerId: string; mimeType?: string; }) { // First, check if we have a record with this hash const existing = await prisma.fileNode.findFirst({ where: { hash: data.hash } }); if (existing) { // 🛡️ DISASTER RECOVERY MODE // The database knows about this file, but the OneDrive link is old. // We update the existing record with the NEW cloud ID. return await prisma.fileNode.update({ where: { id: existing.id }, data: { oneDriveId: data.oneDriveId, // Optional: Update parent if the user chose a new folder during restore parentId: data.parentId || existing.parentId, updatedAt: new Date(), } }); } // ✨ NEW UPLOAD MODE return await prisma.fileNode.create({ data: { id: crypto.randomUUID(), oneDriveId: data.oneDriveId, name: data.name, hash: data.hash, size: data.size, isFolder: false, ownerId: data.ownerId, parentId: data.parentId || null, path: data.name, // Simplified for now metadata: {}, // Placeholder for extractMetadata logic } }); }