ok error
This commit is contained in:
@@ -8,13 +8,35 @@ export async function POST(req: NextRequest) {
|
|||||||
try {
|
try {
|
||||||
const formData = await req.formData();
|
const formData = await req.formData();
|
||||||
const file = formData.get("file") as File | null;
|
const file = formData.get("file") as File | null;
|
||||||
|
// ✅ Validation améliorée du fichier
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Aucun fichier reçu." },
|
{ error: "Aucun fichier reçu." },
|
||||||
{ status: 400 }
|
{ 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 = "";
|
let fileContent = "";
|
||||||
const fileType = file.type;
|
const fileType = file.type;
|
||||||
@@ -22,25 +44,56 @@ export async function POST(req: NextRequest) {
|
|||||||
// --- LOGIQUE D'EXTRACTION DE TEXTE ---
|
// --- LOGIQUE D'EXTRACTION DE TEXTE ---
|
||||||
if (fileType === "application/pdf") {
|
if (fileType === "application/pdf") {
|
||||||
console.log("📄 Traitement PDF en cours...");
|
console.log("📄 Traitement PDF en cours...");
|
||||||
|
console.log("📊 Taille du fichier:", file.size, "bytes");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const buffer = Buffer.from(await file.arrayBuffer());
|
const buffer = Buffer.from(await file.arrayBuffer());
|
||||||
|
console.log("📦 Buffer créé, taille:", buffer.length);
|
||||||
|
|
||||||
const data = await pdf(buffer);
|
const data = await pdf(buffer);
|
||||||
fileContent = data.text || "";
|
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()) {
|
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(
|
return NextResponse.json(
|
||||||
{ error: "Le PDF ne contient pas de texte extractible ou est protégé." },
|
{ error: errorMessage },
|
||||||
{ status: 400 }
|
{ status: 400 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (pdfError) {
|
} 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(
|
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 }
|
{ status: 500 }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ export const useAnonymization = ({
|
|||||||
const textToProcess = sourceText || fileContent || "";
|
const textToProcess = sourceText || fileContent || "";
|
||||||
|
|
||||||
if (!textToProcess.trim()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +71,11 @@ export const useAnonymization = ({
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
if (uploadedFile) {
|
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);
|
formData.append("file", uploadedFile);
|
||||||
} else {
|
} else {
|
||||||
console.log("📝 Traitement texte saisi");
|
console.log("📝 Traitement texte saisi");
|
||||||
@@ -79,13 +85,45 @@ export const useAnonymization = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log("🔍 Appel à /api/process-document avec Presidio...");
|
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", {
|
const response = await fetch("/api/process-document", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
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) {
|
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();
|
const data: ProcessDocumentResponse = await response.json();
|
||||||
@@ -101,27 +139,31 @@ export const useAnonymization = ({
|
|||||||
|
|
||||||
// Extraire les mappings depuis les résultats Presidio (plus d'erreur 'any')
|
// Extraire les mappings depuis les résultats Presidio (plus d'erreur 'any')
|
||||||
if (data.analyzerResults && data.text) {
|
if (data.analyzerResults && data.text) {
|
||||||
const mappings: EntityMapping[] = data.analyzerResults.map((entity: PresidioAnalyzerResult, index: number) => ({
|
const mappings: EntityMapping[] = data.analyzerResults.map(
|
||||||
originalValue: data.text!.substring(entity.start, entity.end),
|
(entity: PresidioAnalyzerResult, index: number) => ({
|
||||||
anonymizedValue: `[${entity.entity_type}${index + 1}]`,
|
originalValue: data.text!.substring(entity.start, entity.end),
|
||||||
entityType: entity.entity_type,
|
anonymizedValue: `[${entity.entity_type}${index + 1}]`,
|
||||||
startIndex: entity.start,
|
entityType: entity.entity_type,
|
||||||
endIndex: entity.end
|
startIndex: entity.start,
|
||||||
}));
|
endIndex: entity.end,
|
||||||
|
})
|
||||||
|
);
|
||||||
setEntityMappings(mappings);
|
setEntityMappings(mappings);
|
||||||
console.log("📋 Entités détectées:", mappings.length);
|
console.log("📋 Entités détectées:", mappings.length);
|
||||||
console.log("🔍 Détails des entités:", mappings);
|
console.log("🔍 Détails des entités:", mappings);
|
||||||
}
|
}
|
||||||
} else if (data.text) {
|
} 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);
|
setOutputText(data.text);
|
||||||
setError("Presidio temporairement indisponible. Texte non anonymisé.");
|
setError("Presidio temporairement indisponible. Texte non anonymisé.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Erreur anonymisation:", error);
|
console.error("❌ Erreur anonymisation complète:", error);
|
||||||
setError(
|
setError(
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? `Erreur Presidio: ${error.message}`
|
? error.message
|
||||||
: "Erreur lors de l'anonymisation avec Presidio"
|
: "Erreur lors de l'anonymisation avec Presidio"
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -47,7 +47,29 @@ export const useFileHandler = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
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();
|
const data = await response.json();
|
||||||
|
|||||||
33
app/page.tsx
33
app/page.tsx
@@ -131,12 +131,12 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Error Message */}
|
{/* Error Message - Version améliorée */}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="bg-red-50 border border-red-200 rounded-xl p-3 sm:p-4 mx-2 sm:mx-0">
|
<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
|
<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"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
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"
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user