Fonctionnalités implémentées : ✅ Authentification locale (email/password) + Azure AD + Manus OAuth ✅ Upload drag-and-drop de fichiers PDF avec suivi en temps réel ✅ Extraction automatique avec Mistral AI (OCR + LLM) ✅ Détection de doublons (fournisseur, numéro, date) ✅ Score de qualité d'extraction (0-100) ✅ Tableau de bord avec statistiques ✅ Liste des factures avec recherche et filtres ✅ Paramètres utilisateur (LLM, keywords, SFTP) ✅ Historique des imports avec logs détaillés ✅ Gestion des utilisateurs (admin) ✅ Export SFTP manuel/automatique ✅ Stockage local avec organisation YYYY-MM ✅ Tests unitaires d'authentification Architecture : - Frontend : React 19 + Vite + TailwindCSS + Radix UI - Backend : Express + tRPC + Drizzle ORM - Base de données : MySQL (6 tables) - IA : Mistral AI pour extraction - Stockage : Local filesystem - Export : SFTP Pages : - Login (choix local/Azure/Manus) - Dashboard (statistiques) - Upload (drag-and-drop) - Invoices (liste avec recherche) - Settings (LLM, keywords, SFTP) - History (logs d'import) - Users (gestion admin)
116 lines
3.1 KiB
TypeScript
116 lines
3.1 KiB
TypeScript
import SftpClient from "ssh2-sftp-client";
|
|
import { localStorageGet } from "./localStorage";
|
|
import { getUserSettings } from "./db";
|
|
|
|
export interface SftpConfig {
|
|
host: string;
|
|
port: number;
|
|
username: string;
|
|
password: string;
|
|
remotePath: string;
|
|
}
|
|
|
|
/**
|
|
* Test SFTP connection
|
|
*/
|
|
export async function testSftpConnection(config: SftpConfig): Promise<boolean> {
|
|
const sftp = new SftpClient();
|
|
|
|
try {
|
|
await sftp.connect({
|
|
host: config.host,
|
|
port: config.port,
|
|
username: config.username,
|
|
password: config.password,
|
|
});
|
|
|
|
console.log("[SFTP] Connection successful");
|
|
return true;
|
|
} catch (error) {
|
|
console.error("[SFTP] Connection failed:", error);
|
|
return false;
|
|
} finally {
|
|
await sftp.end();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export invoice files (PDF + JSON) to SFTP server
|
|
*/
|
|
export async function exportInvoiceToSftp(
|
|
config: SftpConfig,
|
|
pdfFileKey: string,
|
|
jsonFileKey: string | null,
|
|
invoiceDate: Date
|
|
): Promise<void> {
|
|
const sftp = new SftpClient();
|
|
|
|
try {
|
|
// Connect to SFTP
|
|
await sftp.connect({
|
|
host: config.host,
|
|
port: config.port,
|
|
username: config.username,
|
|
password: config.password,
|
|
});
|
|
|
|
console.log("[SFTP] Connected successfully");
|
|
|
|
// Create directory structure: remotePath/YYYY/MM/DD
|
|
const year = invoiceDate.getFullYear();
|
|
const month = String(invoiceDate.getMonth() + 1).padStart(2, "0");
|
|
const day = String(invoiceDate.getDate()).padStart(2, "0");
|
|
|
|
const targetDir = `${config.remotePath}/${year}/${month}/${day}`.replace(/\/+/g, "/");
|
|
|
|
// Ensure directory exists
|
|
await sftp.mkdir(targetDir, true);
|
|
|
|
console.log(`[SFTP] Created directory: ${targetDir}`);
|
|
|
|
// Upload PDF file
|
|
const pdfBuffer = await localStorageGet(pdfFileKey);
|
|
const pdfFileName = pdfFileKey.split("/").pop() || "invoice.pdf";
|
|
const pdfRemotePath = `${targetDir}/${pdfFileName}`;
|
|
|
|
await sftp.put(pdfBuffer, pdfRemotePath);
|
|
console.log(`[SFTP] Uploaded PDF: ${pdfRemotePath}`);
|
|
|
|
// Upload JSON metadata file if exists
|
|
if (jsonFileKey) {
|
|
const jsonBuffer = await localStorageGet(jsonFileKey);
|
|
const jsonFileName = jsonFileKey.split("/").pop() || "metadata.json";
|
|
const jsonRemotePath = `${targetDir}/${jsonFileName}`;
|
|
|
|
await sftp.put(jsonBuffer, jsonRemotePath);
|
|
console.log(`[SFTP] Uploaded JSON: ${jsonRemotePath}`);
|
|
}
|
|
|
|
console.log("[SFTP] Export completed successfully");
|
|
} catch (error) {
|
|
console.error("[SFTP] Export failed:", error);
|
|
throw error;
|
|
} finally {
|
|
await sftp.end();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get SFTP configuration for a user
|
|
*/
|
|
export async function getUserSftpConfig(userId: number): Promise<SftpConfig | null> {
|
|
const settings = await getUserSettings(userId);
|
|
|
|
if (!settings || !settings.sftpHost || !settings.sftpUsername || !settings.sftpPassword) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
host: settings.sftpHost,
|
|
port: settings.sftpPort || 22,
|
|
username: settings.sftpUsername,
|
|
password: settings.sftpPassword,
|
|
remotePath: settings.sftpRemotePath || "/",
|
|
};
|
|
}
|