From f657d62373cc0e46dc0c788ae54f01e7742e6efc Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 14:16:58 +0200 Subject: [PATCH] pages s3 --- app/api/storage/files/route.ts | 15 ++++- app/api/storage/init/route.ts | 5 ++ app/pages/page.tsx | 98 +++++++++++++++++++++++--------- components/carnet/navigation.tsx | 17 ++++-- lib/s3.ts | 16 +++++- 5 files changed, 114 insertions(+), 37 deletions(-) diff --git a/app/api/storage/files/route.ts b/app/api/storage/files/route.ts index 6d8b7db7..9cb57876 100644 --- a/app/api/storage/files/route.ts +++ b/app/api/storage/files/route.ts @@ -32,11 +32,20 @@ export async function GET(request: Request) { return NextResponse.json({ error: 'Folder parameter is required' }, { status: 400 }); } - // Normalize folder name to lowercase to match S3 convention + // Try both lowercase and original case to maintain compatibility + // MinIO/S3 is case-sensitive, so we need to handle both formats const normalizedFolder = folderParam.toLowerCase(); - // List objects for the user in the specified folder - const files = await listUserObjects(userId, normalizedFolder); + console.log(`Listing files for user ${userId} in folder: ${folderParam} (normalized: ${normalizedFolder})`); + + // First try with the exact folder name as provided + let files = await listUserObjects(userId, folderParam); + + // If no files found with original case, try with lowercase + if (files.length === 0 && folderParam !== normalizedFolder) { + console.log(`No files found with original case, trying lowercase: ${normalizedFolder}`); + files = await listUserObjects(userId, normalizedFolder); + } return NextResponse.json(files); } catch (error) { diff --git a/app/api/storage/init/route.ts b/app/api/storage/init/route.ts index b07f156e..ead1b143 100644 --- a/app/api/storage/init/route.ts +++ b/app/api/storage/init/route.ts @@ -10,9 +10,14 @@ export async function POST(request: Request) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } + console.log(`Initializing storage for user: ${session.user.id}`); + // Create folder structure for the user await createUserFolderStructure(session.user.id); + // Update the session to indicate storage is initialized + // This is handled client-side since we can't modify the session directly here + // Return success response return NextResponse.json({ success: true, diff --git a/app/pages/page.tsx b/app/pages/page.tsx index 9c004a63..fa2b7967 100644 --- a/app/pages/page.tsx +++ b/app/pages/page.tsx @@ -45,6 +45,7 @@ interface Contact { export default function CarnetPage() { const { data: session, status } = useSession(); const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); const [layoutMode, setLayoutMode] = useState("item-selection"); const [selectedNote, setSelectedNote] = useState(null); const [isMobile, setIsMobile] = useState(false); @@ -149,22 +150,6 @@ export default function CarnetPage() { fetchContacts(selectedFolder); } else { // For other folders (Notes, etc.), fetch notes - const fetchNotes = async () => { - try { - setIsLoadingNotes(true); - const response = await fetch(`/api/nextcloud/files?folder=${selectedFolder}`); - if (!response.ok) { - throw new Error('Failed to fetch notes'); - } - const data = await response.json(); - setNotes(data); - } catch (error) { - console.error('Error fetching notes:', error); - setNotes([]); - } finally { - setIsLoadingNotes(false); - } - }; fetchNotes(); } }, [selectedFolder, session?.user?.id]); @@ -262,6 +247,74 @@ export default function CarnetPage() { } }; + // Fetch notes based on the selected folder + const fetchNotes = async () => { + try { + setIsLoading(true); + + // Handle folder paths for S3 format - endpoint will try both cases + const response = await fetch(`/api/nextcloud/files?folder=${selectedFolder}`); + + if (response.ok) { + const data = await response.json(); + console.log(`Fetched ${data.length} notes from ${selectedFolder} folder`); + setNotes(data); + } else { + console.error('Error fetching notes:', await response.text()); + setNotes([]); + } + } catch (error) { + console.error('Error fetching notes:', error); + setNotes([]); + } finally { + setIsLoading(false); + } + }; + + // Handle saving changes to a note + const handleSaveNote = async (note: Note) => { + try { + setIsSaving(true); + // Construct API payload - ensure folder is properly set + const payload = { + id: note.id, + title: note.title, + content: note.content, + folder: selectedFolder.toLowerCase(), // Use lowercase for S3 consistency + mime: "text/markdown" + }; + + // Use the API endpoint to save the note + const endpoint = note.id ? '/api/nextcloud/files' : '/api/nextcloud/files'; + const method = note.id ? 'PUT' : 'POST'; + + console.log(`Saving note to ${selectedFolder} using ${method}:`, { + id: note.id, + title: note.title, + folder: selectedFolder.toLowerCase() + }); + + const response = await fetch(endpoint, { + method, + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }); + + if (response.ok) { + // Refresh the list of notes + fetchNotes(); + } else { + console.error('Error saving note:', await response.text()); + } + } catch (error) { + console.error('Error saving note:', error); + } finally { + setIsSaving(false); + } + }; + // Handle panel resizing const handleNavResize = (e: MouseEvent) => { if (!isDraggingNav) return; @@ -642,20 +695,11 @@ export default function CarnetPage() { ) : ( { // Refresh the notes list - fetch(`/api/nextcloud/files?folder=${selectedFolder}`) - .then(response => response.json()) - .then(updatedNotes => { - if (selectedFolder === 'Contacts') { - setContacts(updatedNotes); - } else { - setNotes(updatedNotes); - } - }) - .catch(error => console.error('Error refreshing data:', error)); + fetchNotes(); }} /> )} diff --git a/components/carnet/navigation.tsx b/components/carnet/navigation.tsx index 3e770720..084c6982 100644 --- a/components/carnet/navigation.tsx +++ b/components/carnet/navigation.tsx @@ -77,23 +77,28 @@ export default function Navigation({ nextcloudFolders, onFolderSelect }: Navigat const fetchContactFiles = async () => { try { setIsLoadingContacts(true); + // Use the consistent folder name case that matches S3 structure + // The endpoint will try both cases, but we should prefer the consistent one const response = await fetch('/api/nextcloud/files?folder=Contacts'); if (response.ok) { const files = await response.json(); // Only log the number of files received, not their contents - console.log(`Received ${files.length} files from Nextcloud`); + console.log(`Received ${files.length} files from storage`); // Filter for VCF files and map to ContactFile interface const vcfFiles = files - .filter((file: any) => file.basename.endsWith('.vcf')) + .filter((file: any) => file.basename?.endsWith('.vcf') || file.title?.endsWith('.vcf')) .map((file: any) => ({ - id: file.etag, - filename: file.filename, - basename: file.basename, - lastmod: file.lastmod + id: file.etag || file.id, + filename: file.filename || file.id, + basename: file.basename || file.title, + lastmod: file.lastmod || file.lastModified })); // Only log the number of VCF files processed console.log(`Processed ${vcfFiles.length} VCF files`); setContactFiles(vcfFiles); + } else { + console.error('Error fetching contact files:', await response.text()); + setContactFiles([]); } } catch (error) { console.error('Error fetching contact files:', error); diff --git a/lib/s3.ts b/lib/s3.ts index 32658462..40c3376d 100644 --- a/lib/s3.ts +++ b/lib/s3.ts @@ -123,15 +123,29 @@ export async function deleteObject(key: string) { // Create folder structure (In S3, folders are just prefix notations) export async function createUserFolderStructure(userId: string) { try { - // Define the standard folders to create + // Define the standard folders to create - use lowercase for consistency with S3 operations + // These are the canonical folder names that match what the frontend expects in the "vues" sidebar const folders = ['notes', 'diary', 'health', 'contacts']; + // Also create capitalized versions for backward compatibility with UI components + const capitalizedFolders = ['Notes', 'Diary', 'Health', 'Contacts']; + // For S3, creating a folder means creating an empty object with the folder name as a prefix + // First create lowercase versions (primary storage) for (const folder of folders) { const key = `user-${userId}/${folder}/`; + console.log(`Creating folder: ${key}`); await putObject(key, '', 'application/x-directory'); } + // Then create capitalized versions (for backward compatibility) + for (const folder of capitalizedFolders) { + const key = `user-${userId}/${folder}/`; + console.log(`Creating capitalized folder: ${key}`); + await putObject(key, '', 'application/x-directory'); + } + + console.log(`Successfully created folder structure for user: ${userId}`); return true; } catch (error) { console.error('Error creating folder structure:', error);