234 lines
6.8 KiB
TypeScript
234 lines
6.8 KiB
TypeScript
/**
|
|
* Service API pour communiquer avec le backend
|
|
* Remplace le traitement local par des appels au serveur backend
|
|
*/
|
|
|
|
import type { ExtractionResult, AnalysisResult, ContextResult, ConseilResult } from '../types'
|
|
|
|
const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3001'
|
|
|
|
export interface BackendExtractionResult {
|
|
success: boolean
|
|
documentId: string
|
|
fileName: string
|
|
fileSize: number
|
|
mimeType: string
|
|
processing: {
|
|
ocr: {
|
|
text: string
|
|
confidence: number
|
|
wordCount: number
|
|
}
|
|
ner: {
|
|
identities: any[]
|
|
addresses: any[]
|
|
cniNumbers: any[]
|
|
dates: any[]
|
|
documentType: string
|
|
}
|
|
globalConfidence: number
|
|
}
|
|
extractedData: {
|
|
documentType: string
|
|
identities: any[]
|
|
addresses: any[]
|
|
cniNumbers: any[]
|
|
dates: any[]
|
|
}
|
|
timestamp: string
|
|
}
|
|
|
|
export interface BackendTestFiles {
|
|
success: boolean
|
|
files: Array<{
|
|
name: string
|
|
size: number
|
|
type: string
|
|
lastModified: string
|
|
}>
|
|
}
|
|
|
|
/**
|
|
* Extrait le texte et les entités d'un document via le backend
|
|
*/
|
|
export async function extractDocumentBackend(
|
|
_documentId: string,
|
|
file?: File,
|
|
hooks?: { onOcrProgress?: (progress: number) => void; onLlmProgress?: (progress: number) => void }
|
|
): Promise<ExtractionResult> {
|
|
console.log('🚀 [BACKEND] Début de l\'extraction via le backend...')
|
|
|
|
if (!file) {
|
|
throw new Error('Aucun fichier fourni pour l\'extraction')
|
|
}
|
|
|
|
// Simuler la progression OCR
|
|
if (hooks?.onOcrProgress) {
|
|
hooks.onOcrProgress(0.1)
|
|
console.log('⏳ [BACKEND] Envoi du fichier au backend...')
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append('document', file)
|
|
|
|
try {
|
|
const response = await fetch(`${BACKEND_URL}/api/extract`, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`)
|
|
}
|
|
|
|
const result = await response.json()
|
|
|
|
// Vérifier si c'est le nouveau format JSON standard
|
|
if (!result.document || !result.classification || !result.extraction) {
|
|
throw new Error('Format de réponse backend invalide')
|
|
}
|
|
|
|
console.log('✅ [BACKEND] Extraction terminée avec succès')
|
|
|
|
// Convertir le résultat backend vers le format frontend
|
|
const extractionResult: ExtractionResult = {
|
|
documentId: result.document.id,
|
|
text: result.extraction.text.raw,
|
|
language: result.classification.language,
|
|
documentType: result.classification.documentType,
|
|
identities: result.extraction.entities.persons.map((person: any) => ({
|
|
id: person.id,
|
|
type: 'person',
|
|
firstName: person.firstName,
|
|
lastName: person.lastName,
|
|
confidence: person.confidence || 0.9
|
|
})),
|
|
addresses: result.extraction.entities.addresses.map((address: any) => ({
|
|
id: address.id,
|
|
street: address.street,
|
|
city: address.city,
|
|
postalCode: address.postalCode,
|
|
country: address.country || 'France',
|
|
confidence: address.confidence || 0.9
|
|
})),
|
|
properties: [],
|
|
contracts: [],
|
|
signatures: result.extraction.entities.contractual?.signatures?.map((sig: any) => sig.signatory || 'Signature détectée') || [],
|
|
confidence: result.metadata.quality.globalConfidence,
|
|
confidenceReasons: [
|
|
`OCR: ${Math.round(result.metadata.quality.textExtractionConfidence * 100)}% de confiance`,
|
|
`Texte extrait: ${result.extraction.text.characterCount} caractères`,
|
|
`Entités trouvées: ${result.extraction.entities.persons.length} personnes, ${result.extraction.entities.companies.length} sociétés, ${result.extraction.entities.addresses.length} adresses`,
|
|
`Type détecté: ${result.classification.documentType}`,
|
|
`Traitement backend: ${result.document.uploadTimestamp}`
|
|
]
|
|
}
|
|
|
|
// Extraction terminée
|
|
|
|
console.log('🎉 [BACKEND] Extraction terminée avec succès:', {
|
|
documentType: extractionResult.documentType,
|
|
identitiesCount: extractionResult.identities.length,
|
|
addressesCount: extractionResult.addresses.length,
|
|
confidence: extractionResult.confidence
|
|
})
|
|
|
|
return extractionResult
|
|
|
|
} catch (error) {
|
|
console.error('❌ [BACKEND] Erreur lors de l\'extraction:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Récupère la liste des fichiers de test depuis le backend
|
|
*/
|
|
export async function getTestFilesBackend(): Promise<BackendTestFiles> {
|
|
try {
|
|
const response = await fetch(`${BACKEND_URL}/api/test-files`)
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`)
|
|
}
|
|
|
|
const result: BackendTestFiles = await response.json()
|
|
console.log('📁 [BACKEND] Fichiers de test récupérés:', result.files.length)
|
|
|
|
return result
|
|
} catch (error) {
|
|
console.error('❌ [BACKEND] Erreur lors de la récupération des fichiers de test:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Cache pour le health check
|
|
let backendHealthCache: { isHealthy: boolean; timestamp: number } | null = null
|
|
const HEALTH_CHECK_CACHE_DURATION = 5000 // 5 secondes
|
|
|
|
/**
|
|
* Vérifie la santé du backend avec cache
|
|
*/
|
|
export async function checkBackendHealth(): Promise<boolean> {
|
|
const now = Date.now()
|
|
|
|
// Utiliser le cache si disponible et récent
|
|
if (backendHealthCache && (now - backendHealthCache.timestamp) < HEALTH_CHECK_CACHE_DURATION) {
|
|
return backendHealthCache.isHealthy
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${BACKEND_URL}/api/health`)
|
|
const result = await response.json()
|
|
|
|
const isHealthy = result.status === 'OK'
|
|
console.log('🏥 [BACKEND] Health check:', result.status)
|
|
|
|
// Mettre en cache le résultat
|
|
backendHealthCache = { isHealthy, timestamp: now }
|
|
|
|
return isHealthy
|
|
} catch (error) {
|
|
console.error('❌ [BACKEND] Backend non accessible:', error)
|
|
|
|
// Mettre en cache le résultat négatif
|
|
backendHealthCache = { isHealthy: false, timestamp: now }
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
// API mock pour compatibilité avec l'ancien système
|
|
export const backendDocumentApi = {
|
|
extract: extractDocumentBackend,
|
|
analyze: async (documentId: string): Promise<AnalysisResult> => {
|
|
// Pour l'instant, retourner une analyse basique
|
|
return {
|
|
documentId,
|
|
documentType: 'Document',
|
|
isCNI: false,
|
|
credibilityScore: 0.8,
|
|
summary: 'Analyse en cours...',
|
|
recommendations: []
|
|
}
|
|
},
|
|
getContext: async (documentId: string): Promise<ContextResult> => {
|
|
return {
|
|
documentId,
|
|
lastUpdated: new Date(),
|
|
georisquesData: {},
|
|
cadastreData: {}
|
|
}
|
|
},
|
|
getConseil: async (documentId: string): Promise<ConseilResult> => {
|
|
return {
|
|
documentId,
|
|
analysis: 'Analyse en cours...',
|
|
recommendations: [],
|
|
risks: [],
|
|
nextSteps: [],
|
|
generatedAt: new Date()
|
|
}
|
|
}
|
|
}
|