#!/usr/bin/env php */ require_once __DIR__ . '/../lib/CryptoService.php'; require_once __DIR__ . '/../lib/DatabaseConnection.php'; require_once __DIR__ . '/../lib/helpers.php'; // Vérifier les arguments if ($argc < 3) { error("Usage: " . basename($argv[0]) . " "); error("Exemple: " . basename($argv[0]) . " dva plumeliau"); error("Exemple: " . basename($argv[0]) . " rca amicale"); exit(1); } $environment = strtoupper($argv[1]); $searchTerm = strtolower(trim($argv[2])); if (strlen($searchTerm) < 2) { error("La recherche doit contenir au moins 2 caractères"); exit(1); } try { // Ouvrir le tunnel SSH si nécessaire $tunnelScript = __DIR__ . '/_ssh-tunnel.sh'; exec("$tunnelScript open $environment 2>&1", $output, $exitCode); if ($exitCode !== 0) { error("Impossible d'ouvrir le tunnel SSH"); exit(1); } // Connexion à la base de données $db = new DatabaseConnection($environment); $pdo = $db->connect(); info("Environnement: $environment"); info("Recherche: \"$searchTerm\""); info("Champs: nom, email, adresse1, adresse2, ville\n"); // Pré-filtrage sur les champs en clair (adresse1, adresse2, ville) $stmt = $pdo->prepare(" SELECT e.id, e.encrypted_name, e.encrypted_email, e.adresse1, e.adresse2, e.code_postal, e.ville, e.chk_stripe, COUNT(DISTINCT u.id) as nb_users, COUNT(DISTINCT o.id) as nb_operations FROM entites e LEFT JOIN users u ON u.fk_entite = e.id LEFT JOIN operations o ON o.fk_entite = e.id WHERE e.chk_active = 1 AND (LOWER(e.adresse1) LIKE :search1 OR LOWER(e.adresse2) LIKE :search2 OR LOWER(e.ville) LIKE :search3) GROUP BY e.id ORDER BY e.id "); $searchPattern = '%' . $searchTerm . '%'; $stmt->execute([ 'search1' => $searchPattern, 'search2' => $searchPattern, 'search3' => $searchPattern ]); $preFilteredEntites = $stmt->fetchAll(); // Récupérer TOUTES les entités pour chercher dans les champs chiffrés $stmtAll = $pdo->query(" SELECT e.id, e.encrypted_name, e.encrypted_email, e.adresse1, e.adresse2, e.code_postal, e.ville, e.chk_stripe, COUNT(DISTINCT u.id) as nb_users, COUNT(DISTINCT o.id) as nb_operations FROM entites e LEFT JOIN users u ON u.fk_entite = e.id LEFT JOIN operations o ON o.fk_entite = e.id WHERE e.chk_active = 1 GROUP BY e.id ORDER BY e.id "); $allEntites = $stmtAll->fetchAll(); info("Analyse de " . count($allEntites) . " entités actives...\n"); // Déchiffrer et filtrer $config = DatabaseConfig::getInstance(); $crypto = new CryptoService($config->getEncryptionKey()); $matchedEntites = []; $seenIds = []; // Ajouter d'abord les résultats pré-filtrés (adresse1, adresse2, ville) foreach ($preFilteredEntites as $entite) { $name = $crypto->decryptWithIV($entite['encrypted_name']); $email = $crypto->decryptSearchable($entite['encrypted_email']); // Vérifier aussi dans les champs chiffrés $matches = false; $matchedFields = []; if (stripos($entite['adresse1'], $searchTerm) !== false) { $matches = true; $matchedFields[] = 'adresse1'; } if (stripos($entite['adresse2'], $searchTerm) !== false) { $matches = true; $matchedFields[] = 'adresse2'; } if (stripos($entite['ville'], $searchTerm) !== false) { $matches = true; $matchedFields[] = 'ville'; } if ($name && stripos($name, $searchTerm) !== false) { $matches = true; $matchedFields[] = 'nom'; } if ($email && stripos($email, $searchTerm) !== false) { $matches = true; $matchedFields[] = 'email'; } if ($matches) { $matchedEntites[] = [ 'id' => $entite['id'], 'name' => $name ?? '-', 'email' => $email ?? '-', 'adresse1' => $entite['adresse1'] ?? '-', 'code_postal' => $entite['code_postal'] ?? '-', 'ville' => $entite['ville'] ?? '-', 'stripe' => $entite['chk_stripe'] ? 'Oui' : 'Non', 'nb_users' => $entite['nb_users'], 'nb_operations' => $entite['nb_operations'], 'matched_in' => implode(', ', $matchedFields), ]; $seenIds[$entite['id']] = true; } } // Chercher dans les entités restantes (pour nom et email chiffrés) foreach ($allEntites as $entite) { if (isset($seenIds[$entite['id']])) { continue; // Déjà trouvé } $name = $crypto->decryptWithIV($entite['encrypted_name']); $email = $crypto->decryptSearchable($entite['encrypted_email']); $matches = false; $matchedFields = []; if ($name && stripos($name, $searchTerm) !== false) { $matches = true; $matchedFields[] = 'nom'; } if ($email && stripos($email, $searchTerm) !== false) { $matches = true; $matchedFields[] = 'email'; } if ($matches) { $matchedEntites[] = [ 'id' => $entite['id'], 'name' => $name ?? '-', 'email' => $email ?? '-', 'adresse1' => $entite['adresse1'] ?? '-', 'code_postal' => $entite['code_postal'] ?? '-', 'ville' => $entite['ville'] ?? '-', 'stripe' => $entite['chk_stripe'] ? 'Oui' : 'Non', 'nb_users' => $entite['nb_users'], 'nb_operations' => $entite['nb_operations'], 'matched_in' => implode(', ', $matchedFields), ]; $seenIds[$entite['id']] = true; } } if (empty($matchedEntites)) { warning("\nAucune entité trouvée avec: \"$searchTerm\""); exit(0); } // Affichage title("RÉSULTATS - " . count($matchedEntites) . " entité(s) trouvée(s)"); // Préparer les données pour le tableau $tableData = []; $lineNumber = 1; foreach ($matchedEntites as $entite) { $tableData[] = [ 'line' => $lineNumber++, 'id' => $entite['id'], 'name' => truncate($entite['name'], 30), 'ville' => truncate($entite['ville'], 20), 'cp' => $entite['code_postal'], 'users' => $entite['nb_users'], 'ops' => $entite['nb_operations'], 'stripe' => $entite['stripe'], 'match' => $entite['matched_in'], ]; } table( [ 'line' => '#', 'id' => 'ID', 'name' => 'Nom', 'ville' => 'Ville', 'cp' => 'CP', 'users' => 'Users', 'ops' => 'Ops', 'stripe' => 'Stripe', 'match' => 'Trouvé dans', ], $tableData, true ); success("Recherche terminée"); // Menu interactif if (count($matchedEntites) > 0) { echo "\n"; echo color("═══════════════════════════════════════\n", 'cyan'); echo color(" Actions disponibles\n", 'bold'); echo color("═══════════════════════════════════════\n", 'cyan'); echo " 1. Détail d'une entité\n"; echo " 2. Opérations d'une entité\n"; echo " 3. Membres d'une entité\n"; echo " 0. Quitter\n"; echo color("═══════════════════════════════════════\n", 'cyan'); echo color("\nVotre choix: ", 'yellow'); $handle = fopen('php://stdin', 'r'); $choice = fgets($handle); $choice = $choice !== false ? trim($choice) : ''; if ($choice === '0' || $choice === '') { echo "\n"; info("Au revoir !"); exit(0); } // Demander le numéro de ligne (sauf si une seule trouvée) $entiteId = null; if (count($matchedEntites) === 1) { $entiteId = $matchedEntites[0]['id']; info("\nEntité sélectionnée: #$entiteId - " . $matchedEntites[0]['name']); } else { echo color("\nEntrez le n° de ligne (1-" . count($matchedEntites) . "): ", 'yellow'); $lineInput = trim(fgets($handle)); if (!is_numeric($lineInput) || (int)$lineInput < 1 || (int)$lineInput > count($matchedEntites)) { fclose($handle); error("Numéro de ligne invalide (doit être entre 1 et " . count($matchedEntites) . ")"); exit(1); } $lineNumber = (int)$lineInput; $entiteId = $matchedEntites[$lineNumber - 1]['id']; info("\nEntité sélectionnée: #$entiteId - " . $matchedEntites[$lineNumber - 1]['name']); } fclose($handle); echo "\n"; // Exécuter l'action choisie switch ($choice) { case '1': // Détail de l'entité $decryptEntiteScript = __DIR__ . '/decrypt-entite'; passthru("$decryptEntiteScript $environment $entiteId"); break; case '2': // Opérations de l'entité $listOperationsScript = __DIR__ . '/list-operations'; passthru("$listOperationsScript $environment --entite=$entiteId"); break; case '3': // Membres de l'entité $listUsersScript = __DIR__ . '/list-users'; passthru("$listUsersScript $environment --entite=$entiteId"); break; default: error("Choix invalide: $choice"); exit(1); } } } catch (Exception $e) { error("Erreur: " . $e->getMessage()); exit(1); }