PDO::ERRMODE_EXCEPTION]); } return $pdo; } $DATA_TYPES = [ 'leads' => ['name' => 'Leads', 'icon' => 'fa-users', 'color' => '#6366f1', 'table' => 'admin.import_leads', 'unique' => 'email', 'desc' => 'Contacts pour prospection', 'fields' => ['email','first_name','last_name','company','phone','title','country','linkedin','industry']], 'send_data' => ['name' => 'Send Data', 'icon' => 'fa-paper-plane', 'color' => '#3b82f6', 'table' => 'admin.send_data', 'unique' => null, 'desc' => 'Data pour envoi campagne', 'fields' => ['email','first_name','last_name','company','phone','title','country','custom1','custom2','custom3']], 'inbox' => ['name' => 'Inbox', 'icon' => 'fa-inbox', 'color' => '#8b5cf6', 'table' => 'admin.inbox_accounts', 'unique' => 'email', 'desc' => 'Comptes email IMAP/SMTP', 'fields' => ['email','password','imap_host','imap_port','smtp_host','smtp_port','provider','proxy']], 'seed_inbox' => ['name' => 'Seed Inbox', 'icon' => 'fa-seedling', 'color' => '#10b981', 'table' => 'admin.seed_inboxes', 'unique' => 'email', 'desc' => 'Comptes pour seeding/warmup', 'fields' => ['email','password','imap_host','imap_port','smtp_host','smtp_port','provider']], 'o365' => ['name' => 'Office 365', 'icon' => 'fa-microsoft', 'color' => '#0078d4', 'table' => 'admin.office_accounts', 'unique' => 'email', 'desc' => 'Comptes Microsoft 365', 'fields' => ['email','password','status']], 'smtp' => ['name' => 'SMTP', 'icon' => 'fa-server', 'color' => '#f59e0b', 'table' => 'admin.smtp_servers', 'unique' => 'host', 'desc' => 'Serveurs envoi email', 'fields' => ['host','port','username','password','encryption','from_email','daily_limit']], 'domains' => ['name' => 'Domains', 'icon' => 'fa-globe', 'color' => '#ec4899', 'table' => 'admin.monitored_domains', 'unique' => 'domain', 'desc' => 'Domaines à monitorer', 'fields' => ['domain','provider','status']], 'warmup' => ['name' => 'Warmup', 'icon' => 'fa-fire', 'color' => '#ef4444', 'table' => 'admin.warmup_accounts', 'unique' => 'email', 'desc' => 'Comptes en réchauffement', 'fields' => ['email','password','smtp_host','smtp_port','daily_limit']] ]; $SOURCES = ['manual','linkedin','hunter','apollo','zoominfo','snov','dropcontact','lusha','scraping','api','csv_upload','crm_export','webform','other']; function ensureTables() { $pdo = getDB(); $pdo->exec("CREATE TABLE IF NOT EXISTS admin.import_jobs (id SERIAL PRIMARY KEY, data_type VARCHAR(50), filename VARCHAR(255), source VARCHAR(100) DEFAULT 'csv_upload', tags TEXT, total_rows INTEGER DEFAULT 0, processed_rows INTEGER DEFAULT 0, imported_rows INTEGER DEFAULT 0, duplicate_rows INTEGER DEFAULT 0, error_rows INTEGER DEFAULT 0, status VARCHAR(20) DEFAULT 'pending', speed_rows_sec DECIMAL(10,2) DEFAULT 0, started_at TIMESTAMP, completed_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); $pdo->exec("CREATE TABLE IF NOT EXISTS admin.import_leads (id SERIAL PRIMARY KEY, job_id INTEGER, email VARCHAR(255) NOT NULL, first_name VARCHAR(100), last_name VARCHAR(100), company VARCHAR(255), phone VARCHAR(50), title VARCHAR(255), country VARCHAR(100), linkedin VARCHAR(500), industry VARCHAR(100), source VARCHAR(100), tags TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); $pdo->exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_leads_email ON admin.import_leads(email)"); $pdo->exec("CREATE TABLE IF NOT EXISTS admin.send_data (id SERIAL PRIMARY KEY, job_id INTEGER, email VARCHAR(255) NOT NULL, first_name VARCHAR(100), last_name VARCHAR(100), company VARCHAR(255), phone VARCHAR(50), title VARCHAR(255), country VARCHAR(100), custom1 VARCHAR(500), custom2 VARCHAR(500), custom3 VARCHAR(500), source VARCHAR(100), tags TEXT, status VARCHAR(50) DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); $pdo->exec("CREATE INDEX IF NOT EXISTS idx_send_email ON admin.send_data(email)"); $pdo->exec("CREATE TABLE IF NOT EXISTS admin.inbox_accounts (id SERIAL PRIMARY KEY, job_id INTEGER, email VARCHAR(255) NOT NULL, password VARCHAR(255), imap_host VARCHAR(255), imap_port INTEGER DEFAULT 993, smtp_host VARCHAR(255), smtp_port INTEGER DEFAULT 587, provider VARCHAR(100), proxy VARCHAR(255), status VARCHAR(50) DEFAULT 'active', source VARCHAR(100), tags TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); $pdo->exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_inbox_email ON admin.inbox_accounts(email)"); $pdo->exec("CREATE TABLE IF NOT EXISTS admin.seed_inboxes (id SERIAL PRIMARY KEY, job_id INTEGER, email VARCHAR(255) NOT NULL, password VARCHAR(255), imap_host VARCHAR(255), imap_port INTEGER DEFAULT 993, smtp_host VARCHAR(255), smtp_port INTEGER DEFAULT 587, provider VARCHAR(100), status VARCHAR(50) DEFAULT 'active', source VARCHAR(100), tags TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)"); $pdo->exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_seed_email ON admin.seed_inboxes(email)"); } if (isset($_GET['action']) || isset($_POST['action'])) { header('Content-Type: application/json'); ensureTables(); $action = $_GET['action'] ?? $_POST['action']; $pdo = getDB(); try { switch ($action) { case 'stats': echo json_encode(['success' => true, 'stats' => [ 'jobs' => $pdo->query("SELECT COUNT(*) FROM admin.import_jobs")->fetchColumn(), 'leads' => $pdo->query("SELECT COUNT(*) FROM admin.import_leads")->fetchColumn(), 'send_data' => $pdo->query("SELECT COUNT(*) FROM admin.send_data")->fetchColumn(), 'inbox' => $pdo->query("SELECT COUNT(*) FROM admin.inbox_accounts")->fetchColumn(), 'seeds' => $pdo->query("SELECT COUNT(*) FROM admin.seed_inboxes")->fetchColumn(), 'today' => $pdo->query("SELECT COUNT(*) FROM admin.import_jobs WHERE created_at > CURRENT_DATE")->fetchColumn(), ]]); break; case 'upload': if (!isset($_FILES['file'])) { echo json_encode(['success' => false, 'error' => 'No file']); break; } $dataType = $_POST['data_type'] ?? 'leads'; $source = $_POST['source'] ?? 'csv_upload'; $tags = $_POST['tags'] ?? ''; $hasHeader = ($_POST['has_header'] ?? '1') === '1'; $uploadDir = '/tmp/imports/'; @mkdir($uploadDir, 0777, true); $origName = $_FILES['file']['name']; $ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION)); $filename = uniqid('imp_') . '.' . $ext; $filepath = $uploadDir . $filename; move_uploaded_file($_FILES['file']['tmp_name'], $filepath); if (in_array($ext, ['xlsx', 'xls'])) { $csvFile = $filepath . '.csv'; exec("python3 -c \"import pandas as pd; pd.read_excel('$filepath').to_csv('$csvFile', index=False)\" 2>/dev/null || ssconvert '$filepath' '$csvFile' 2>/dev/null"); if (file_exists($csvFile)) { unlink($filepath); $filepath = $csvFile; $filename .= '.csv'; } } $lineCount = 0; $handle = fopen($filepath, 'r'); $sample = fread($handle, 8192); rewind($handle); while (!feof($handle)) { if (trim(fgets($handle)) !== '') $lineCount++; } fclose($handle); if ($hasHeader) $lineCount = max(0, $lineCount - 1); $delims = [';' => 0, ',' => 0, "\t" => 0, '|' => 0]; foreach ($delims as $d => &$c) $c = substr_count($sample, $d); arsort($delims); $delimiter = key($delims); $stmt = $pdo->prepare("INSERT INTO admin.import_jobs (data_type, filename, source, tags, total_rows, status) VALUES (?, ?, ?, ?, ?, 'ready') RETURNING id"); $stmt->execute([$dataType, $filename, $source, $tags, $lineCount]); $jobId = $stmt->fetchColumn(); $preview = ['headers' => null, 'rows' => []]; $handle = fopen($filepath, 'r'); $row = 0; while (($line = fgets($handle)) !== false && $row < 6) { $line = trim($line); if ($line === '') continue; $data = str_getcsv($line, $delimiter); if ($row === 0 && $hasHeader) $preview['headers'] = $data; else $preview['rows'][] = $data; $row++; } fclose($handle); echo json_encode(['success' => true, 'job_id' => $jobId, 'filename' => $filename, 'total_rows' => $lineCount, 'delimiter' => $delimiter, 'preview' => $preview]); break; case 'process': $jobId = intval($_POST['job_id'] ?? 0); $delimiter = $_POST['delimiter'] ?? ';'; $hasHeader = ($_POST['has_header'] ?? '1') === '1'; $columnMap = json_decode($_POST['column_map'] ?? '{}', true); $dedupe = ($_POST['dedupe'] ?? '1') === '1'; $chunkSize = max(500, min(50000, intval($_POST['chunk_size'] ?? 5000))); $stmt = $pdo->prepare("SELECT * FROM admin.import_jobs WHERE id = ?"); $stmt->execute([$jobId]); $job = $stmt->fetch(PDO::FETCH_ASSOC); if (!$job) { echo json_encode(['success' => false, 'error' => 'Job not found']); break; } $filepath = '/tmp/imports/' . $job['filename']; if (!file_exists($filepath)) { echo json_encode(['success' => false, 'error' => 'File not found']); break; } global $DATA_TYPES; $typeConfig = $DATA_TYPES[$job['data_type']] ?? $DATA_TYPES['leads']; $table = $typeConfig['table']; $uniqueField = $typeConfig['unique']; $pdo->exec("UPDATE admin.import_jobs SET status='processing', started_at=NOW() WHERE id=$jobId"); $handle = fopen($filepath, 'r'); if ($hasHeader) fgets($handle); $rowNum = $imported = $duplicates = $errors = 0; $startTime = microtime(true); $batch = []; $seenKeys = []; while (($line = fgets($handle)) !== false) { $line = trim($line); if ($line === '') continue; $rowNum++; $data = str_getcsv($line, $delimiter); $mapped = ['job_id' => $jobId, 'source' => $job['source'], 'tags' => $job['tags']]; foreach ($columnMap as $idx => $field) { if ($field && $field !== 'skip') $mapped[$field] = trim($data[$idx] ?? ''); } $key = $mapped[$uniqueField ?? 'email'] ?? $mapped['email'] ?? ''; if (!$key && $uniqueField) { $errors++; continue; } if ($dedupe && $uniqueField && isset($seenKeys[$key])) { $duplicates++; continue; } if ($uniqueField) $seenKeys[$key] = true; $batch[] = $mapped; if (count($batch) >= $chunkSize) { $imported += bulkInsert($pdo, $table, $batch, $uniqueField); $batch = []; } } if (count($batch) > 0) $imported += bulkInsert($pdo, $table, $batch, $uniqueField); fclose($handle); @unlink($filepath); $elapsed = microtime(true) - $startTime; $speed = $rowNum / max($elapsed, 0.001); $pdo->exec("UPDATE admin.import_jobs SET processed_rows=$rowNum, imported_rows=$imported, duplicate_rows=$duplicates, error_rows=$errors, speed_rows_sec=".round($speed,2).", status='completed', completed_at=NOW() WHERE id=$jobId"); echo json_encode(['success' => true, 'processed' => $rowNum, 'imported' => $imported, 'duplicates' => $duplicates, 'errors' => $errors, 'speed' => round($speed, 2), 'elapsed' => round($elapsed, 2)]); break; case 'history': $jobs = $pdo->query("SELECT * FROM admin.import_jobs ORDER BY created_at DESC LIMIT 100")->fetchAll(PDO::FETCH_ASSOC); echo json_encode(['success' => true, 'jobs' => $jobs]); break; default: echo json_encode(['success' => false, 'error' => 'Unknown action']); } } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } exit; } function bulkInsert($pdo, $table, $batch, $uniqueField) { if (empty($batch)) return 0; $columns = array_keys($batch[0]); $values = []; $placeholders = []; foreach ($batch as $row) { $ph = []; foreach ($columns as $col) { $values[] = $row[$col] ?? null; $ph[] = '?'; } $placeholders[] = '(' . implode(',', $ph) . ')'; } $sql = "INSERT INTO $table (" . implode(',', $columns) . ") VALUES " . implode(',', $placeholders); if ($uniqueField) $sql .= " ON CONFLICT ($uniqueField) DO NOTHING"; try { $stmt = $pdo->prepare($sql); $stmt->execute($values); return $stmt->rowCount(); } catch (Exception $e) { return 0; } } ensureTables(); ?>
=$t['desc']?>
CSV, Excel, TXT
| ID | Type | Source | Tags | Total | Imported | Speed | Status | Date |
|---|