176 lines
5.2 KiB
TypeScript
176 lines
5.2 KiB
TypeScript
import { useState } from "react";
|
|
|
|
interface EntityMapping {
|
|
originalValue: string;
|
|
anonymizedValue: string;
|
|
entityType: string;
|
|
startIndex: number;
|
|
endIndex: number;
|
|
}
|
|
|
|
// Nouvelle interface pour les résultats de Presidio Analyzer
|
|
interface PresidioAnalyzerResult {
|
|
entity_type: string;
|
|
start: number;
|
|
end: number;
|
|
score: number;
|
|
analysis_explanation?: {
|
|
recognizer: string;
|
|
pattern_name?: string;
|
|
pattern?: string;
|
|
validation_result?: boolean;
|
|
};
|
|
}
|
|
|
|
// Interface pour la réponse de l'API
|
|
interface ProcessDocumentResponse {
|
|
text?: string;
|
|
anonymizedText?: string;
|
|
piiCount?: number;
|
|
analyzerResults?: PresidioAnalyzerResult[];
|
|
error?: string;
|
|
}
|
|
|
|
interface AnonymizationLogicProps {
|
|
sourceText: string;
|
|
fileContent: string;
|
|
uploadedFile: File | null;
|
|
setOutputText: (text: string) => void;
|
|
setError: (error: string | null) => void;
|
|
setEntityMappings: (mappings: EntityMapping[]) => void;
|
|
}
|
|
|
|
export const useAnonymization = ({
|
|
sourceText,
|
|
fileContent,
|
|
uploadedFile,
|
|
setOutputText,
|
|
setError,
|
|
setEntityMappings,
|
|
}: AnonymizationLogicProps) => {
|
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
|
|
const anonymizeData = async () => {
|
|
const textToProcess = sourceText || fileContent || "";
|
|
|
|
if (!textToProcess.trim()) {
|
|
setError(
|
|
"Veuillez saisir du texte à anonymiser ou télécharger un fichier"
|
|
);
|
|
return;
|
|
}
|
|
|
|
setIsProcessing(true);
|
|
setError(null);
|
|
setOutputText("");
|
|
setEntityMappings([]);
|
|
|
|
try {
|
|
console.log("🚀 Début anonymisation avec Presidio");
|
|
|
|
const formData = new FormData();
|
|
|
|
if (uploadedFile) {
|
|
console.log("📁 Traitement fichier:", {
|
|
name: uploadedFile.name,
|
|
type: uploadedFile.type,
|
|
size: uploadedFile.size
|
|
});
|
|
formData.append("file", uploadedFile);
|
|
} else {
|
|
console.log("📝 Traitement texte saisi");
|
|
const textBlob = new Blob([textToProcess], { type: "text/plain" });
|
|
const textFile = new File([textBlob], "input.txt", { type: "text/plain" });
|
|
formData.append("file", textFile);
|
|
}
|
|
|
|
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) {
|
|
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();
|
|
console.log("📊 Réponse API:", data);
|
|
|
|
if (data.error) {
|
|
throw new Error(data.error);
|
|
}
|
|
|
|
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,
|
|
})
|
|
);
|
|
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é"
|
|
);
|
|
setOutputText(data.text);
|
|
setError("Presidio temporairement indisponible. Texte non anonymisé.");
|
|
}
|
|
} catch (error) {
|
|
console.error("❌ Erreur anonymisation complète:", error);
|
|
setError(
|
|
error instanceof Error
|
|
? error.message
|
|
: "Erreur lors de l'anonymisation avec Presidio"
|
|
);
|
|
} finally {
|
|
setIsProcessing(false);
|
|
}
|
|
};
|
|
|
|
return { anonymizeData, isProcessing };
|
|
};
|