diff --git a/app/api/storage/init/folder/route.ts b/app/api/storage/init/folder/route.ts index d87283c..9f2ff75 100644 --- a/app/api/storage/init/folder/route.ts +++ b/app/api/storage/init/folder/route.ts @@ -1,8 +1,7 @@ import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; -import { putObject } from '@/lib/s3'; -import { S3_CONFIG } from '@/lib/s3'; +import { putObject, listUserObjects, getObjectContent } from '@/lib/s3'; /** * API endpoint to create a specific folder for a user @@ -37,24 +36,46 @@ export async function POST(request: Request) { console.log(`Creating folder ${folderLowercase} for user ${userId}`); try { - // In S3/MinIO, folders are just prefixes, so we don't need to create an empty object. - // We only create a placeholder file to mark the folder as initialized. - const placeholderKey = `user-${userId}/${folderLowercase}/.placeholder`; - const placeholderContent = `Folder initialized at ${new Date().toISOString()}`; + // Check if folder already exists by listing objects + const existingFiles = await listUserObjects(userId, folderLowercase); + // If folder already has files, it exists and we don't need to create anything + if (existingFiles.length > 0) { + console.log(`Folder ${folderLowercase} already exists with ${existingFiles.length} file(s)`); + return NextResponse.json({ + success: true, + message: `Folder ${folderLowercase} already exists`, + existingFiles: existingFiles.length + }); + } + + // Check if placeholder already exists + const placeholderKey = `user-${userId}/${folderLowercase}/.placeholder`; + const placeholderExists = await getObjectContent(placeholderKey); + + if (placeholderExists) { + console.log(`Folder ${folderLowercase} already initialized`); + return NextResponse.json({ + success: true, + message: `Folder ${folderLowercase} already initialized` + }); + } + + // Folder is empty and no placeholder exists, create one + const placeholderContent = `Folder initialized at ${new Date().toISOString()}`; await putObject(placeholderKey, placeholderContent, 'text/plain'); - console.log(`Successfully created folder: user-${userId}/${folderLowercase}/`); + console.log(`Successfully initialized folder: user-${userId}/${folderLowercase}/`); // Return success return NextResponse.json({ success: true, - message: `Folder ${folderLowercase} created successfully` + message: `Folder ${folderLowercase} initialized successfully` }); } catch (error) { - console.error(`Error creating folder ${folderLowercase}:`, error); + console.error(`Error initializing folder ${folderLowercase}:`, error); return NextResponse.json({ - error: 'Failed to create folder', + error: 'Failed to initialize folder', details: error instanceof Error ? error.message : String(error) }, { status: 500 }); } diff --git a/lib/s3.ts b/lib/s3.ts index 80f3d0a..781f882 100644 --- a/lib/s3.ts +++ b/lib/s3.ts @@ -110,18 +110,34 @@ export async function deleteObject(key: string): Promise { * List objects for a user in a specific folder */ export async function listUserObjects(userId: string, folder: string): Promise> { - const prefix = `user-${userId}/${folder}/`; + // Normalize folder name to lowercase for consistency + const normalizedFolder = folder.toLowerCase(); + const prefix = `user-${userId}/${normalizedFolder}/`; + const command = new ListObjectsV2Command({ Bucket: S3_CONFIG.bucket, Prefix: prefix, - Delimiter: '/' + // Don't use Delimiter to get all objects, not just "folders" }); const response = await s3Client.send(command); const objects = response.Contents || []; + // Filter out: + // - Objects that are "folders" (end with /) + // - Placeholder files + // - Objects that don't match the exact prefix (shouldn't happen but safety check) return objects - .filter(obj => obj.Key && !obj.Key.endsWith('/') && !obj.Key.includes('.placeholder')) + .filter(obj => { + if (!obj.Key) return false; + // Exclude folder markers (end with /) + if (obj.Key.endsWith('/')) return false; + // Exclude placeholder files + if (obj.Key.includes('.placeholder')) return false; + // Ensure it matches our prefix + if (!obj.Key.startsWith(prefix)) return false; + return true; + }) .map(obj => ({ key: obj.Key!, name: obj.Key!.split('/').pop() || obj.Key!, @@ -155,25 +171,38 @@ export function getPublicUrl(filePath: string, bucket?: string): string { /** * Create standard folder structure for a user * In S3/MinIO, folders don't need to be explicitly created - they exist as prefixes. - * We only create a placeholder file to mark the folder as initialized. + * We only create a placeholder file if the folder doesn't exist or is empty. */ export async function createUserFolderStructure(userId: string): Promise { const folders = ['notes', 'diary', 'health', 'contacts']; for (const folder of folders) { try { - // In S3/MinIO, folders are just prefixes, so we don't need to create an empty object. - // We only create a placeholder file to mark the folder as initialized. + // Check if folder already exists by listing objects + const existingFiles = await listUserObjects(userId, folder); + + // If folder already has files, skip creating placeholder + if (existingFiles.length > 0) { + console.log(`Folder user-${userId}/${folder}/ already exists with ${existingFiles.length} file(s), skipping creation`); + continue; + } + + // Check if placeholder already exists const placeholderKey = `user-${userId}/${folder}/.placeholder`; + const placeholderExists = await getObjectContent(placeholderKey); - // Use a simple text content for the placeholder + if (placeholderExists) { + console.log(`Folder user-${userId}/${folder}/ already initialized, skipping`); + continue; + } + + // Folder is empty and no placeholder exists, create one const placeholderContent = `Folder initialized at ${new Date().toISOString()}`; - await putObject(placeholderKey, placeholderContent, 'text/plain'); - console.log(`Created folder: user-${userId}/${folder}/`); + console.log(`Initialized folder: user-${userId}/${folder}/`); } catch (error) { - console.error(`Error creating folder ${folder} for user ${userId}:`, error); + console.error(`Error initializing folder ${folder} for user ${userId}:`, error); // Continue with other folders even if one fails } }