diff --git a/.gitignore b/.gitignore index 8cad2f7..17e4806 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,7 @@ next-env.d.ts /data/*.db /data/backups/* + +# Uploaded images +/public/uploads/notes/* +!/public/uploads/notes/.gitkeep diff --git a/public/uploads/notes/.gitkeep b/public/uploads/notes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/app/api/notes/images/upload/route.ts b/src/app/api/notes/images/upload/route.ts new file mode 100644 index 0000000..ffb7403 --- /dev/null +++ b/src/app/api/notes/images/upload/route.ts @@ -0,0 +1,71 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth'; +import { writeFile, mkdir } from 'fs/promises'; +import { join } from 'path'; +import { existsSync } from 'fs'; + +export async function POST(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }); + } + + const formData = await request.formData(); + const file = formData.get('file') as File; + + if (!file) { + return NextResponse.json( + { error: 'Aucun fichier fourni' }, + { status: 400 } + ); + } + + // Vérifier que c'est bien une image + if (!file.type.startsWith('image/')) { + return NextResponse.json( + { error: 'Le fichier doit être une image' }, + { status: 400 } + ); + } + + // Limiter la taille à 10MB + const maxSize = 10 * 1024 * 1024; // 10MB + if (file.size > maxSize) { + return NextResponse.json( + { error: "L'image est trop grande (max 10MB)" }, + { status: 400 } + ); + } + + // Créer le dossier de stockage s'il n'existe pas + const uploadsDir = join(process.cwd(), 'public', 'uploads', 'notes'); + if (!existsSync(uploadsDir)) { + await mkdir(uploadsDir, { recursive: true }); + } + + // Générer un nom de fichier unique + const timestamp = Date.now(); + const randomStr = Math.random().toString(36).substring(2, 15); + const extension = file.name.split('.').pop() || 'png'; + const filename = `${timestamp}-${randomStr}.${extension}`; + const filepath = join(uploadsDir, filename); + + // Convertir le File en Buffer et l'écrire + const bytes = await file.arrayBuffer(); + const buffer = Buffer.from(bytes); + await writeFile(filepath, buffer); + + // Retourner l'URL relative de l'image + const imageUrl = `/uploads/notes/${filename}`; + + return NextResponse.json({ url: imageUrl }); + } catch (error) { + console.error('Error uploading image:', error); + return NextResponse.json( + { error: "Erreur lors de l'upload de l'image" }, + { status: 500 } + ); + } +} diff --git a/src/components/notes/MarkdownEditor.tsx b/src/components/notes/MarkdownEditor.tsx index c9210c3..f587bc5 100644 --- a/src/components/notes/MarkdownEditor.tsx +++ b/src/components/notes/MarkdownEditor.tsx @@ -279,6 +279,22 @@ const createMarkdownComponents = (