- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
318 lines
10 KiB
PHP
Executable File
318 lines
10 KiB
PHP
Executable File
#!/usr/bin/env php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Script de recherche d'entité par nom/adresse/ville/email
|
|
* Usage: ./search-entite <environment> <search_term>
|
|
*/
|
|
|
|
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]) . " <environment> <search_term>");
|
|
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);
|
|
}
|