diff --git a/app/api/process-document/route.ts b/app/api/process-document/route.ts index c4f2dab..50b57ae 100644 --- a/app/api/process-document/route.ts +++ b/app/api/process-document/route.ts @@ -8,13 +8,35 @@ export async function POST(req: NextRequest) { try { const formData = await req.formData(); const file = formData.get("file") as File | null; + // ✅ Validation améliorée du fichier if (!file) { return NextResponse.json( { error: "Aucun fichier reçu." }, { status: 400 } ); } - console.log("📁 Fichier reçu:", file.name, "| Type:", file.type); + + // Vérifications supplémentaires + if (file.size === 0) { + return NextResponse.json( + { error: "Le fichier est vide (0 bytes)." }, + { status: 400 } + ); + } + + if (file.size > 50 * 1024 * 1024) { // 50MB + return NextResponse.json( + { error: "Le fichier est trop volumineux (max 50MB)." }, + { status: 400 } + ); + } + + console.log("📁 Fichier reçu:", { + name: file.name, + type: file.type, + size: `${(file.size / 1024 / 1024).toFixed(2)} MB`, + lastModified: new Date(file.lastModified).toISOString() + }); let fileContent = ""; const fileType = file.type; @@ -22,25 +44,56 @@ export async function POST(req: NextRequest) { // --- LOGIQUE D'EXTRACTION DE TEXTE --- if (fileType === "application/pdf") { console.log("📄 Traitement PDF en cours..."); + console.log("📊 Taille du fichier:", file.size, "bytes"); + try { const buffer = Buffer.from(await file.arrayBuffer()); + console.log("📦 Buffer créé, taille:", buffer.length); + const data = await pdf(buffer); fileContent = data.text || ""; - console.log("✅ Extraction PDF réussie, longueur:", fileContent.length); - // ✅ Vérification supplémentaire + console.log("✅ Extraction PDF réussie, longueur:", fileContent.length); + console.log("📄 Nombre de pages:", data.numpages); + console.log("ℹ️ Info PDF:", data.info?.Title || "Titre non disponible"); + + // ✅ Vérification améliorée if (!fileContent.trim()) { - console.log("⚠️ PDF vide ou non lisible"); + console.log("⚠️ PDF vide - Détails:", { + pages: data.numpages, + metadata: data.metadata, + info: data.info, + extractedLength: fileContent.length + }); + + // Détecter si c'est un PDF scanné + const isScanned = data.info?.Creator?.includes('RICOH') || + data.info?.Creator?.includes('Canon') || + data.info?.Creator?.includes('HP') || + data.info?.Producer?.includes('Scanner') || + (data.numpages > 0 && fileContent.length < 50); + + const errorMessage = isScanned + ? `Ce PDF semble être un document scanné (créé par: ${data.info?.Creator}). Les documents scannés contiennent des images de texte, pas du texte extractible.\n\n💡 Solutions :\n- Utilisez un PDF créé depuis Word/Google Docs\n- Appliquez l'OCR avec Adobe Acrobat\n- Recréez le document au lieu de le scanner` + : `Le PDF ne contient pas de texte extractible.\n\nCela peut être dû à :\n- PDF scanné (image uniquement)\n- PDF protégé\n- PDF avec texte en images\n- Nombre de pages: ${data.numpages}`; + return NextResponse.json( - { error: "Le PDF ne contient pas de texte extractible ou est protégé." }, + { error: errorMessage }, { status: 400 } ); } } catch (pdfError) { - console.error("❌ Erreur PDF détaillée:", pdfError); + console.error("❌ Erreur PDF détaillée:", { + message: pdfError instanceof Error ? pdfError.message : "Erreur inconnue", + stack: pdfError instanceof Error ? pdfError.stack : undefined, + fileName: file.name, + fileSize: file.size, + fileType: file.type + }); + return NextResponse.json( { - error: `Erreur traitement PDF: ${pdfError instanceof Error ? pdfError.message : "Erreur inconnue"}. Vérifiez que le PDF n'est pas protégé ou corrompu.`, + error: `Impossible de traiter ce PDF (${file.name}). Erreur: ${pdfError instanceof Error ? pdfError.message : "Erreur inconnue"}. Vérifiez que le PDF n'est pas protégé, corrompu ou scanné.`, }, { status: 500 } ); diff --git a/app/components/AnonymizationLogic.tsx b/app/components/AnonymizationLogic.tsx index 9afb8ef..74c1417 100644 --- a/app/components/AnonymizationLogic.tsx +++ b/app/components/AnonymizationLogic.tsx @@ -54,7 +54,9 @@ export const useAnonymization = ({ const textToProcess = sourceText || fileContent || ""; if (!textToProcess.trim()) { - setError("Veuillez saisir du texte à anonymiser ou télécharger un fichier"); + setError( + "Veuillez saisir du texte à anonymiser ou télécharger un fichier" + ); return; } @@ -69,7 +71,11 @@ export const useAnonymization = ({ const formData = new FormData(); if (uploadedFile) { - console.log("📁 Traitement fichier:", uploadedFile.name); + console.log("📁 Traitement fichier:", { + name: uploadedFile.name, + type: uploadedFile.type, + size: uploadedFile.size + }); formData.append("file", uploadedFile); } else { console.log("📝 Traitement texte saisi"); @@ -79,13 +85,45 @@ export const useAnonymization = ({ } console.log("🔍 Appel à /api/process-document avec Presidio..."); + console.log("📦 FormData préparée:", Array.from(formData.entries())); + const response = await fetch("/api/process-document", { method: "POST", body: formData, }); + console.log("📡 Réponse reçue:", { + ok: response.ok, + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()) + }); + if (!response.ok) { - throw new Error(`Erreur API: ${response.status}`); + let errorMessage = `Erreur HTTP: ${response.status}`; + + try { + const responseText = await response.text(); + console.log("📄 Contenu de l'erreur:", responseText); + + if (responseText.trim()) { + try { + const errorData = JSON.parse(responseText); + if (errorData.error) { + errorMessage = errorData.error; + console.log("✅ Message détaillé récupéré:", errorMessage); + } + } catch (jsonError) { + console.error("❌ Erreur parsing JSON:", jsonError); // ✅ Utiliser la variable + console.error("❌ Réponse non-JSON:", responseText); + errorMessage = `Erreur ${response.status}: Réponse invalide du serveur`; + } + } + } catch (readError) { + console.error("❌ Impossible de lire la réponse:", readError); + } + + throw new Error(errorMessage); } const data: ProcessDocumentResponse = await response.json(); @@ -98,30 +136,34 @@ export const useAnonymization = ({ if (data.anonymizedText) { console.log("✅ Anonymisation réussie avec Presidio"); setOutputText(data.anonymizedText); - + // Extraire les mappings depuis les résultats Presidio (plus d'erreur 'any') if (data.analyzerResults && data.text) { - const mappings: EntityMapping[] = data.analyzerResults.map((entity: PresidioAnalyzerResult, index: number) => ({ - originalValue: data.text!.substring(entity.start, entity.end), - anonymizedValue: `[${entity.entity_type}${index + 1}]`, - entityType: entity.entity_type, - startIndex: entity.start, - endIndex: entity.end - })); + const mappings: EntityMapping[] = data.analyzerResults.map( + (entity: PresidioAnalyzerResult, index: number) => ({ + originalValue: data.text!.substring(entity.start, entity.end), + anonymizedValue: `[${entity.entity_type}${index + 1}]`, + entityType: entity.entity_type, + startIndex: entity.start, + endIndex: entity.end, + }) + ); setEntityMappings(mappings); console.log("📋 Entités détectées:", mappings.length); console.log("🔍 Détails des entités:", mappings); } } else if (data.text) { - console.log("⚠️ Fallback: Presidio non disponible, texte original retourné"); + console.log( + "⚠️ Fallback: Presidio non disponible, texte original retourné" + ); setOutputText(data.text); setError("Presidio temporairement indisponible. Texte non anonymisé."); } } catch (error) { - console.error("❌ Erreur anonymisation:", error); + console.error("❌ Erreur anonymisation complète:", error); setError( error instanceof Error - ? `Erreur Presidio: ${error.message}` + ? error.message : "Erreur lors de l'anonymisation avec Presidio" ); } finally { @@ -130,4 +172,4 @@ export const useAnonymization = ({ }; return { anonymizeData, isProcessing }; -}; \ No newline at end of file +}; diff --git a/app/components/FileHandler.tsx b/app/components/FileHandler.tsx index 4238675..bd0addb 100644 --- a/app/components/FileHandler.tsx +++ b/app/components/FileHandler.tsx @@ -47,7 +47,29 @@ export const useFileHandler = ({ }); if (!response.ok) { - throw new Error(`Erreur HTTP: ${response.status}`); + // ✅ Récupérer le message d'erreur détaillé du serveur + let errorMessage = `Erreur HTTP: ${response.status}`; + + try { + const responseText = await response.text(); + console.log("🔍 Réponse brute du serveur:", responseText); + + try { + const errorData = JSON.parse(responseText); + if (errorData.error) { + errorMessage = errorData.error; + console.log("✅ Message détaillé récupéré:", errorMessage); + } + } catch (jsonError) { + console.error("❌ Erreur parsing JSON:", jsonError); + console.error("❌ Réponse non-JSON:", responseText); + errorMessage = `Erreur ${response.status}: ${responseText || 'Réponse invalide du serveur'}`; + } + } catch (readError) { + console.error("❌ Impossible de lire la réponse:", readError); + } + + throw new Error(errorMessage); } const data = await response.json(); diff --git a/app/page.tsx b/app/page.tsx index 134401b..9947ae1 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -131,12 +131,12 @@ export default function Home() { )} - {/* Error Message */} + {/* Error Message - Version améliorée */} {error && (
-
+
-

{error}

+
+

+ {error.includes('scanné') ? '📄 PDF Scanné Détecté' : + error.includes('HTTP') ? '🚨 Erreur de Traitement' : + '⚠️ Erreur'} +

+
+ {error.split('\n').map((line, index) => ( +
0 ? 'mt-2' : ''}> + {line.startsWith('💡') ? ( +
+
+ {line} +
+
+ ) : line.startsWith('-') ? ( +
+ • {line.substring(1).trim()} +
+ ) : ( +
{line}
+ )} +
+ ))} +
+
)}