This commit is contained in:
nBiqoz
2025-07-28 21:15:41 +02:00
parent dc734e08f0
commit 7a086c4749
4 changed files with 169 additions and 27 deletions

View File

@@ -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 }
);

View File

@@ -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 };
};
};

View File

@@ -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();

View File

@@ -131,12 +131,12 @@ export default function Home() {
</div>
)}
{/* Error Message */}
{/* Error Message - Version améliorée */}
{error && (
<div className="bg-red-50 border border-red-200 rounded-xl p-3 sm:p-4 mx-2 sm:mx-0">
<div className="flex items-center space-x-2">
<div className="flex items-start space-x-3">
<svg
className="w-4 h-4 sm:w-5 sm:h-5 text-red-500 flex-shrink-0"
className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
@@ -148,7 +148,32 @@ export default function Home() {
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<p className="text-red-700 text-xs sm:text-sm font-medium break-words">{error}</p>
<div className="flex-1">
<h3 className="text-red-800 text-sm font-semibold mb-2">
{error.includes('scanné') ? '📄 PDF Scanné Détecté' :
error.includes('HTTP') ? '🚨 Erreur de Traitement' :
'⚠️ Erreur'}
</h3>
<div className="text-red-700 text-xs sm:text-sm leading-relaxed">
{error.split('\n').map((line, index) => (
<div key={index} className={index > 0 ? 'mt-2' : ''}>
{line.startsWith('💡') ? (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mt-3">
<div className="text-blue-800 font-medium text-sm">
{line}
</div>
</div>
) : line.startsWith('-') ? (
<div className="ml-4 text-blue-700">
{line.substring(1).trim()}
</div>
) : (
<div>{line}</div>
)}
</div>
))}
</div>
</div>
</div>
</div>
)}