db = Database::getInstance(); $this->appConfig = AppConfig::getInstance(); } public function getUsers(): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur) // Récupérer le rôle de l'utilisateur depuis la base de données $userId = Session::getUserId(); $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$userId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2') { // Supposons que 1 et 2 sont des rôles admin Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $stmt = $this->db->prepare(' SELECT u.id, u.encrypted_email, u.encrypted_name, u.first_name, u.fk_role as role, u.fk_entite, u.chk_active, u.created_at, u.updated_at, e.encrypted_name as entite_name FROM users u LEFT JOIN entites e ON u.fk_entite = e.id ORDER BY u.created_at DESC '); $stmt->execute(); $users = $stmt->fetchAll(PDO::FETCH_ASSOC); // Déchiffrement des données sensibles pour chaque utilisateur foreach ($users as &$user) { $user['email'] = ApiService::decryptSearchableData($user['encrypted_email']); $user['name'] = ApiService::decryptData($user['encrypted_name']); if (!empty($user['entite_name'])) { $user['entite_name'] = ApiService::decryptData($user['entite_name']); } // Suppression des champs chiffrés unset($user['encrypted_email']); unset($user['encrypted_name']); } Response::json([ 'status' => 'success', 'users' => $users ]); } catch (PDOException $e) { LogService::log('Erreur lors de la récupération des utilisateurs GeoSector', [ 'level' => 'error', 'error' => $e->getMessage() ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function getUserById(string $id): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur ou utilisateur lui-même) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2' && $currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $stmt = $this->db->prepare(' SELECT u.id, u.encrypted_email, u.encrypted_name, u.first_name, u.sect_name, u.encrypted_phone, u.encrypted_mobile, u.fk_role as role, u.fk_entite, u.chk_alert_email, u.chk_suivi, u.date_naissance, u.date_embauche, u.chk_active, u.created_at, u.updated_at, e.encrypted_name as entite_name, e.adresse1, e.adresse2, e.cp, e.ville, e.fk_region FROM users u LEFT JOIN entites e ON u.fk_entite = e.id WHERE u.id = ? '); $stmt->execute([$id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé' ], 404); return; } // Déchiffrement des données sensibles $user['email'] = ApiService::decryptSearchableData($user['encrypted_email']); $user['name'] = ApiService::decryptData($user['encrypted_name']); $user['phone'] = ApiService::decryptData($user['encrypted_phone'] ?? ''); $user['mobile'] = ApiService::decryptData($user['encrypted_mobile'] ?? ''); if (!empty($user['entite_name'])) { $user['entite_name'] = ApiService::decryptData($user['entite_name']); } // Suppression des champs chiffrés unset($user['encrypted_email']); unset($user['encrypted_name']); unset($user['encrypted_phone']); unset($user['encrypted_mobile']); Response::json([ 'status' => 'success', 'user' => $user ]); } catch (PDOException $e) { LogService::log('Erreur lors de la récupération de l\'utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function createUser(): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2') { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $data = Request::getJson(); $currentUserId = Session::getUserId(); // Validation des données requises if (!isset($data['email'], $data['name'])) { Response::json([ 'status' => 'error', 'message' => 'Email et nom requis' ], 400); return; } $email = trim(strtolower($data['email'])); $name = trim($data['name']); $firstName = isset($data['first_name']) ? trim($data['first_name']) : ''; $role = isset($data['role']) ? (int)$data['role'] : 1; $entiteId = isset($data['fk_entite']) ? (int)$data['fk_entite'] : 1; // Récupérer les paramètres de gestion de l'entité $entiteStmt = $this->db->prepare(' SELECT chk_mdp_manuel, chk_username_manuel, code_postal, ville FROM entites WHERE id = ? '); $entiteStmt->execute([$entiteId]); $entiteConfig = $entiteStmt->fetch(PDO::FETCH_ASSOC); if (!$entiteConfig) { Response::json([ 'status' => 'error', 'message' => 'Entité non trouvée' ], 404); return; } $chkMdpManuel = (int)$entiteConfig['chk_mdp_manuel']; $chkUsernameManuel = (int)$entiteConfig['chk_username_manuel']; // Vérification des longueurs d'entrée if (strlen($email) > 75 || strlen($name) > 50) { Response::json([ 'status' => 'error', 'message' => 'Email ou nom trop long' ], 400); return; } // Validation de l'email if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { Response::json([ 'status' => 'error', 'message' => 'Format d\'email invalide' ], 400); return; } // Chiffrement des données sensibles $encryptedEmail = ApiService::encryptSearchableData($email); $encryptedName = ApiService::encryptData($name); // Vérification de l'existence de l'email $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_email = ?'); $checkStmt->execute([$encryptedEmail]); if ($checkStmt->fetch()) { Response::json([ 'status' => 'error', 'message' => 'Cet email est déjà utilisé' ], 409); return; } // Gestion du USERNAME selon chk_username_manuel $encryptedUsername = ''; if ($chkUsernameManuel === 1) { // Username manuel obligatoire if (!isset($data['username']) || empty(trim($data['username']))) { Response::json([ 'status' => 'error', 'message' => 'Le nom d\'utilisateur est requis pour cette entité' ], 400); return; } $username = trim(strtolower($data['username'])); // Validation du format du username if (!preg_match('/^[a-z][a-z0-9._-]{9,29}$/', $username)) { Response::json([ 'status' => 'error', 'message' => 'Format du nom d\'utilisateur invalide (10-30 caractères, commence par une lettre, caractères autorisés: a-z, 0-9, ., -, _)' ], 400); return; } $encryptedUsername = ApiService::encryptSearchableData($username); // Vérification de l'unicité du username $checkUsernameStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_user_name = ?'); $checkUsernameStmt->execute([$encryptedUsername]); if ($checkUsernameStmt->fetch()) { Response::json([ 'status' => 'error', 'message' => 'Ce nom d\'utilisateur est déjà utilisé dans GeoSector' ], 409); return; } } else { // Génération automatique du username $username = ApiService::generateUserName( $this->db, $name, $entiteConfig['code_postal'] ?? '00000', $entiteConfig['ville'] ?? 'ville', 10 ); $encryptedUsername = ApiService::encryptSearchableData($username); } // Gestion du MOT DE PASSE selon chk_mdp_manuel $password = ''; $passwordHash = ''; if ($chkMdpManuel === 1) { // Mot de passe manuel obligatoire if (!isset($data['password']) || empty($data['password'])) { Response::json([ 'status' => 'error', 'message' => 'Le mot de passe est requis pour cette entité' ], 400); return; } $password = $data['password']; // Validation du mot de passe (minimum 8 caractères) if (strlen($password) < 8) { Response::json([ 'status' => 'error', 'message' => 'Le mot de passe doit contenir au moins 8 caractères' ], 400); return; } $passwordHash = password_hash($password, PASSWORD_DEFAULT); } else { // Génération automatique du mot de passe $password = ApiService::generateSecurePassword(); $passwordHash = password_hash($password, PASSWORD_DEFAULT); } // Préparation des champs optionnels $phone = isset($data['phone']) ? ApiService::encryptData(trim($data['phone'])) : null; $mobile = isset($data['mobile']) ? ApiService::encryptData(trim($data['mobile'])) : null; $sectName = isset($data['sect_name']) ? trim($data['sect_name']) : ''; $alertEmail = isset($data['chk_alert_email']) ? (int)$data['chk_alert_email'] : 1; $suivi = isset($data['chk_suivi']) ? (int)$data['chk_suivi'] : 0; $dateNaissance = isset($data['date_naissance']) ? $data['date_naissance'] : null; $dateEmbauche = isset($data['date_embauche']) ? $data['date_embauche'] : null; // Insertion en base de données $stmt = $this->db->prepare(' INSERT INTO users ( encrypted_email, encrypted_user_name, user_pass_hash, encrypted_name, first_name, sect_name, encrypted_phone, encrypted_mobile, fk_role, fk_entite, chk_alert_email, chk_suivi, date_naissance, date_embauche, created_at, fk_user_creat, chk_active ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?, 1 ) '); $stmt->execute([ $encryptedEmail, $encryptedUsername, $passwordHash, $encryptedName, $firstName, $sectName, $phone, $mobile, $role, $entiteId, $alertEmail, $suivi, $dateNaissance, $dateEmbauche, $currentUserId ]); $userId = $this->db->lastInsertId(); // Envoi des emails séparés pour plus de sécurité // 1er email : TOUJOURS envoyer l'identifiant (username) $usernameEmailData = [ 'email' => $email, 'username' => $username, 'name' => $name ]; ApiService::sendEmail($email, $name, 'welcome_username', $usernameEmailData); // 2ème email : Envoyer le mot de passe (toujours, qu'il soit manuel ou généré) // Attendre un peu entre les deux emails pour éviter qu'ils arrivent dans le mauvais ordre sleep(1); $passwordEmailData = [ 'email' => $email, 'password' => $password, 'name' => $name ]; ApiService::sendEmail($email, $name, 'welcome_password', $passwordEmailData); LogService::log('Utilisateur GeoSector créé', [ 'level' => 'info', 'createdBy' => $currentUserId, 'newUserId' => $userId, 'email' => $email, 'username' => $username, 'usernameManual' => $chkUsernameManuel === 1 ? 'oui' : 'non', 'passwordManual' => $chkMdpManuel === 1 ? 'oui' : 'non', 'emailsSent' => '2 emails (username + password)' ]); // Préparer la réponse avec les informations de connexion si générées automatiquement $responseData = [ 'status' => 'success', 'message' => 'Utilisateur créé avec succès', 'id' => $userId ]; // Ajouter le username dans la réponse (toujours, car nécessaire pour la connexion) $responseData['username'] = $username; // Ajouter le mot de passe seulement si généré automatiquement if ($chkMdpManuel === 0) { $responseData['password'] = $password; } Response::json($responseData, 201); } catch (PDOException $e) { LogService::log('Erreur lors de la création d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'code' => $e->getCode() ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function updateUser(string $id): void { Session::requireAuth(); // Vérification des droits d'accès (rôle administrateur ou utilisateur lui-même) $currentUserId = Session::getUserId(); // Récupérer le rôle de l'utilisateur depuis la base de données $stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); $userRole = $result ? $result['fk_role'] : null; if ($userRole != '1' && $userRole != '2' && $currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Accès non autorisé' ], 403); return; } try { $data = Request::getJson(); // Vérification qu'il y a des données à mettre à jour if (empty($data)) { Response::json([ 'status' => 'error', 'message' => 'Aucune donnée à mettre à jour' ], 400); return; } // Construction de la requête UPDATE dynamique $updateFields = []; $params = ['id' => $id]; // Traitement des champs à chiffrer if (isset($data['email'])) { // Vérification que l'email n'est pas déjà utilisé par un autre utilisateur $email = trim(strtolower($data['email'])); $encryptedEmail = ApiService::encryptSearchableData($email); $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_email = ? AND id != ?'); $checkStmt->execute([$encryptedEmail, $id]); if ($checkStmt->fetch()) { Response::json([ 'status' => 'error', 'message' => 'Cet email est déjà utilisé par un autre utilisateur' ], 409); return; } $updateFields[] = "encrypted_email = :encrypted_email"; $params['encrypted_email'] = $encryptedEmail; } if (isset($data['name'])) { $updateFields[] = "encrypted_name = :encrypted_name"; $params['encrypted_name'] = ApiService::encryptData(trim($data['name'])); } if (isset($data['phone'])) { $updateFields[] = "encrypted_phone = :encrypted_phone"; $params['encrypted_phone'] = ApiService::encryptData(trim($data['phone'])); } if (isset($data['mobile'])) { $updateFields[] = "encrypted_mobile = :encrypted_mobile"; $params['encrypted_mobile'] = ApiService::encryptData(trim($data['mobile'])); } // Traitement des champs non chiffrés $nonEncryptedFields = [ 'first_name', 'sect_name', 'fk_role', 'fk_entite', 'chk_alert_email', 'chk_suivi', 'date_naissance', 'date_embauche', 'chk_active' ]; foreach ($nonEncryptedFields as $field) { if (isset($data[$field])) { $updateFields[] = "$field = :$field"; $params[$field] = is_string($data[$field]) ? trim($data[$field]) : $data[$field]; } } // Mise à jour du mot de passe si fourni if (isset($data['password']) && !empty($data['password'])) { if (strlen($data['password']) < 8) { Response::json([ 'status' => 'error', 'message' => 'Le mot de passe doit contenir au moins 8 caractères' ], 400); return; } $updateFields[] = "user_pass_hash = :password"; $params['password'] = password_hash($data['password'], PASSWORD_DEFAULT); } // Ajout des champs de mise à jour $updateFields[] = "updated_at = NOW()"; $updateFields[] = "fk_user_modif = :modifier_id"; $params['modifier_id'] = $currentUserId; if (!empty($updateFields)) { $sql = 'UPDATE users SET ' . implode(', ', $updateFields) . ' WHERE id = :id'; $stmt = $this->db->prepare($sql); $stmt->execute($params); if ($stmt->rowCount() === 0) { Response::json([ 'status' => 'warning', 'message' => 'Aucune modification effectuée' ]); return; } LogService::log('Utilisateur GeoSector mis à jour', [ 'level' => 'info', 'modifiedBy' => $currentUserId, 'userId' => $id, 'fields' => array_keys($data), ]); Response::json([ 'status' => 'success', 'message' => 'Utilisateur mis à jour avec succès' ]); } else { Response::json([ 'status' => 'warning', 'message' => 'Aucune donnée valide à mettre à jour' ]); } } catch (PDOException $e) { LogService::log('Erreur lors de la mise à jour d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function deleteUser(string $id): void { Session::requireAuth(); $currentUserId = Session::getUserId(); // Récupérer les infos de l'utilisateur courant $stmt = $this->db->prepare('SELECT fk_role, fk_entite FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $currentUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$currentUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur courant non trouvé' ], 403); return; } $userRole = (int)$currentUser['fk_role']; $userEntite = $currentUser['fk_entite']; // Empêcher la suppression de son propre compte if ($currentUserId == $id) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez pas supprimer votre propre compte' ], 400); return; } // Récupérer l'utilisateur cible $stmt2 = $this->db->prepare('SELECT fk_entite FROM users WHERE id = ?'); $stmt2->execute([$id]); $userToDelete = $stmt2->fetch(PDO::FETCH_ASSOC); if (!$userToDelete) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur cible non trouvé' ], 404); return; } // Contrôle des droits if ($userRole === 1) { Response::json([ 'status' => 'error', 'message' => "Vous n'avez pas le droit de supprimer un utilisateur" ], 403); return; } elseif ($userRole === 2) { if ($userEntite != $userToDelete['fk_entite']) { Response::json([ 'status' => 'error', 'message' => "Vous n'avez pas le droit de supprimer un utilisateur d'une autre amicale" ], 403); return; } } // fk_role > 2 : tout est permis (hors auto-suppression) // ——— Gestion du transfert éventuel ——— $transferTo = isset($_GET['transfer_to']) ? trim($_GET['transfer_to']) : null; if ($transferTo) { try { // Transférer TOUS les passages de l'utilisateur vers l'utilisateur désigné $stmt3 = $this->db->prepare(' UPDATE ope_pass SET fk_user = :new_user_id WHERE fk_user = :delete_user_id '); $stmt3->execute([ 'new_user_id' => $transferTo, 'delete_user_id' => $id ]); $transferredCount = $stmt3->rowCount(); LogService::log('Passages transférés avant suppression utilisateur', [ 'level' => 'info', 'from_user' => $id, 'to_user' => $transferTo, 'passages_transferred' => $transferredCount ]); } catch (PDOException $e) { Response::json([ 'status' => 'error', 'message' => 'Erreur lors du transfert des passages', 'error' => $e->getMessage() ], 500); return; } } // —— Suppression réelle de l'utilisateur —— try { // Supprimer les enregistrements dépendants dans ope_users $stmtOpeUsers = $this->db->prepare('DELETE FROM ope_users WHERE fk_user = ?'); $stmtOpeUsers->execute([$id]); // Supprimer les enregistrements dépendants dans ope_users_sectors $stmtOpeUsersSectors = $this->db->prepare('DELETE FROM ope_users_sectors WHERE fk_user = ?'); $stmtOpeUsersSectors->execute([$id]); $stmt = $this->db->prepare('DELETE FROM users WHERE id = ?'); $stmt->execute([$id]); if ($stmt->rowCount() === 0) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé ou déjà supprimé' ], 404); return; } LogService::log('Utilisateur GeoSector supprimé', [ 'level' => 'info', 'deletedBy' => $currentUserId, 'userId' => $id, 'passage_transfer' => $transferTo ? "Tous les passages transférés vers utilisateur $transferTo" : 'Aucun transfert' ]); Response::json([ 'status' => 'success', 'message' => 'Utilisateur supprimé avec succès' ]); } catch (PDOException $e) { LogService::log('Erreur lors de la suppression d\'un utilisateur GeoSector', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function resetPassword(string $id): void { Session::requireAuth(); $currentUserId = Session::getUserId(); // Récupérer les infos de l'utilisateur courant $stmt = $this->db->prepare('SELECT fk_role, fk_entite, chk_active FROM users WHERE id = ?'); $stmt->execute([$currentUserId]); $currentUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$currentUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur courant non trouvé' ], 403); return; } // Vérifier que l'utilisateur courant est actif if ($currentUser['chk_active'] != 1) { Response::json([ 'status' => 'error', 'message' => 'Votre compte n\'est pas actif' ], 403); return; } $userRole = (int)$currentUser['fk_role']; $userEntite = $currentUser['fk_entite']; // Récupérer l'utilisateur cible $stmt = $this->db->prepare(' SELECT id, encrypted_email, encrypted_name, fk_entite, chk_active FROM users WHERE id = ? '); $stmt->execute([$id]); $targetUser = $stmt->fetch(PDO::FETCH_ASSOC); if (!$targetUser) { Response::json([ 'status' => 'error', 'message' => 'Utilisateur non trouvé' ], 404); return; } // Vérifier que l'utilisateur est actif if ($targetUser['chk_active'] != 1) { Response::json([ 'status' => 'error', 'message' => 'L\'utilisateur n\'est pas actif' ], 400); return; } // Contrôle des droits selon le rôle if ($userRole === 1) { // Role 1 : peut uniquement réinitialiser son propre mot de passe if ($currentUserId != $id) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez réinitialiser que votre propre mot de passe' ], 403); return; } } elseif ($userRole === 2) { // Role 2 : peut réinitialiser les mots de passe de sa propre entité if ($userEntite != $targetUser['fk_entite']) { Response::json([ 'status' => 'error', 'message' => 'Vous ne pouvez réinitialiser que les mots de passe des utilisateurs de votre entité' ], 403); return; } } // Role > 2 : peut tout faire try { // Déchiffrement des données $email = ApiService::decryptSearchableData($targetUser['encrypted_email']); $name = ApiService::decryptData($targetUser['encrypted_name']); // Génération d'un nouveau mot de passe sécurisé $newPassword = ApiService::generateSecurePassword(); $passwordHash = password_hash($newPassword, PASSWORD_DEFAULT); // Mise à jour du mot de passe en base de données $updateStmt = $this->db->prepare(' UPDATE users SET user_pass_hash = :password, updated_at = NOW(), fk_user_modif = :modifier_id WHERE id = :id '); $updateStmt->execute([ 'password' => $passwordHash, 'modifier_id' => $currentUserId, 'id' => $id ]); // Envoi de l'email avec le nouveau mot de passe ApiService::sendEmail($email, $name, 'password_reset', ['password' => $newPassword]); LogService::log('Mot de passe réinitialisé', [ 'level' => 'info', 'resetBy' => $currentUserId, 'userId' => $id, 'email' => $email ]); Response::json([ 'status' => 'success', 'message' => 'Mot de passe réinitialisé avec succès. Un email a été envoyé à l\'utilisateur.' ]); } catch (PDOException $e) { LogService::log('Erreur lors de la réinitialisation du mot de passe', [ 'level' => 'error', 'error' => $e->getMessage(), 'userId' => $id ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur' ], 500); } } public function checkUsername(): void { Session::requireAuth(); try { $data = Request::getJson(); // Validation de la présence du username if (!isset($data['username']) || empty(trim($data['username']))) { Response::json([ 'status' => 'error', 'message' => 'Username requis pour la vérification' ], 400); return; } $username = trim(strtolower($data['username'])); // Validation du format du username if (!preg_match('/^[a-z][a-z0-9._-]{9,29}$/', $username)) { Response::json([ 'status' => 'error', 'message' => 'Format invalide : 10-30 caractères, commence par une lettre, caractères autorisés: a-z, 0-9, ., -, _', 'available' => false ], 400); return; } // Chiffrement du username pour la recherche $encryptedUsername = ApiService::encryptSearchableData($username); // Vérification de l'existence dans la base $stmt = $this->db->prepare(' SELECT id, encrypted_name, fk_entite FROM users WHERE encrypted_user_name = ? LIMIT 1 '); $stmt->execute([$encryptedUsername]); $existingUser = $stmt->fetch(PDO::FETCH_ASSOC); if ($existingUser) { // Username déjà pris - générer des suggestions $baseName = substr($username, 0, -2); // Enlever les 2 derniers caractères $suggestions = []; // Génération de 3 suggestions $suggestions[] = $username . '_' . rand(10, 99); $suggestions[] = $baseName . rand(100, 999); // Suggestion avec l'année courante $year = date('y'); $suggestions[] = $username . $year; // Vérifier que les suggestions sont aussi disponibles $availableSuggestions = []; foreach ($suggestions as $suggestion) { $encryptedSuggestion = ApiService::encryptSearchableData($suggestion); $checkStmt = $this->db->prepare('SELECT id FROM users WHERE encrypted_user_name = ?'); $checkStmt->execute([$encryptedSuggestion]); if (!$checkStmt->fetch()) { $availableSuggestions[] = $suggestion; } } // Si aucune suggestion n'est disponible, en générer d'autres if (empty($availableSuggestions)) { for ($i = 0; $i < 3; $i++) { $randomSuffix = rand(1000, 9999); $availableSuggestions[] = $baseName . $randomSuffix; } } Response::json([ 'status' => 'success', 'available' => false, 'message' => 'Ce nom d\'utilisateur est déjà utilisé', 'suggestions' => array_slice($availableSuggestions, 0, 3) ]); } else { // Username disponible Response::json([ 'status' => 'success', 'available' => true, 'message' => 'Nom d\'utilisateur disponible', 'username' => $username ]); } } catch (PDOException $e) { LogService::log('Erreur lors de la vérification du username', [ 'level' => 'error', 'error' => $e->getMessage() ]); Response::json([ 'status' => 'error', 'message' => 'Erreur serveur lors de la vérification' ], 500); } } // Méthodes auxiliaires private function validateUpdateData(array $data): ?string { // Validation de l'email if (isset($data['email'])) { if (!filter_var(trim($data['email']), FILTER_VALIDATE_EMAIL)) { return 'Format d\'email invalide'; } } // Validation du nom if (isset($data['name']) && strlen(trim($data['name'])) < 2) { return 'Le nom doit contenir au moins 2 caractères'; } // Validation du téléphone if (isset($data['phone']) && !empty($data['phone'])) { if (!preg_match('/^[0-9+\s()-]{6,20}$/', trim($data['phone']))) { return 'Format de téléphone invalide'; } } // Validation du mobile if (isset($data['mobile']) && !empty($data['mobile'])) { if (!preg_match('/^[0-9+\s()-]{6,20}$/', trim($data['mobile']))) { return 'Format de mobile invalide'; } } return null; } }