Ajout du dossier api avec la géolocalisation automatique des casernes de pompiers
This commit is contained in:
632
api/src/Controllers/EntiteController.php
Normal file
632
api/src/Controllers/EntiteController.php
Normal file
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
require_once __DIR__ . '/../Services/LogService.php';
|
||||
require_once __DIR__ . '/../Services/ApiService.php';
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Database;
|
||||
use AppConfig;
|
||||
use Request;
|
||||
use Response;
|
||||
use Session;
|
||||
use LogService;
|
||||
use ApiService;
|
||||
use Exception;
|
||||
|
||||
class EntiteController {
|
||||
private PDO $db;
|
||||
private AppConfig $appConfig;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = Database::getInstance();
|
||||
$this->appConfig = AppConfig::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle entité (amicale) si elle n'existe pas déjà avec le code postal spécifié
|
||||
*
|
||||
* @param string $name Nom de l'amicale
|
||||
* @param string $postalCode Code postal
|
||||
* @param string $cityName Nom de la ville
|
||||
* @return array|false Tableau contenant l'ID de l'entité créée ou false en cas d'erreur
|
||||
* @throws Exception Si une entité existe déjà avec ce code postal
|
||||
*/
|
||||
public function createEntite(string $name, string $postalCode, string $cityName): array|false {
|
||||
try {
|
||||
// Vérification que le code postal n'existe pas déjà
|
||||
$stmt = $this->db->prepare('SELECT id FROM entites WHERE code_postal = ?');
|
||||
$stmt->execute([$postalCode]);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
throw new Exception('Une amicale existe déjà sur ce code postal');
|
||||
}
|
||||
|
||||
// Chiffrement du nom
|
||||
$encryptedName = ApiService::encryptData($name);
|
||||
|
||||
// Insertion de la nouvelle entité
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO entites (
|
||||
encrypted_name,
|
||||
code_postal,
|
||||
ville,
|
||||
fk_type,
|
||||
created_at,
|
||||
chk_active
|
||||
) VALUES (?, ?, ?, 1, NOW(), 1)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
$encryptedName,
|
||||
$postalCode,
|
||||
$cityName
|
||||
]);
|
||||
|
||||
$entiteId = $this->db->lastInsertId();
|
||||
|
||||
if (!$entiteId) {
|
||||
throw new Exception('Erreur lors de la création de l\'entité');
|
||||
}
|
||||
|
||||
LogService::log('Création d\'une nouvelle entité GeoSector', [
|
||||
'level' => 'info',
|
||||
'entiteId' => $entiteId,
|
||||
'name' => $name,
|
||||
'postalCode' => $postalCode,
|
||||
'cityName' => $cityName
|
||||
]);
|
||||
|
||||
return [
|
||||
'id' => $entiteId,
|
||||
'name' => $name,
|
||||
'postalCode' => $postalCode,
|
||||
'cityName' => $cityName
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la création de l\'entité GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'name' => $name,
|
||||
'postalCode' => $postalCode,
|
||||
'cityName' => $cityName
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une entité par son ID
|
||||
*
|
||||
* @param int $id ID de l'entité
|
||||
* @return array|false Données de l'entité ou false si non trouvée
|
||||
*/
|
||||
public function getEntiteById(int $id): array|false {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
FROM entites
|
||||
WHERE id = ? AND chk_active = 1
|
||||
');
|
||||
|
||||
$stmt->execute([$id]);
|
||||
$entite = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$entite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Déchiffrement du nom
|
||||
$entite['name'] = ApiService::decryptData($entite['encrypted_name']);
|
||||
unset($entite['encrypted_name']);
|
||||
|
||||
return $entite;
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la récupération de l\'entité GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'id' => $id
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une entité par son code postal
|
||||
*
|
||||
* @param string $postalCode Code postal
|
||||
* @return array|false Données de l'entité ou false si non trouvée
|
||||
*/
|
||||
public function getEntiteByPostalCode(string $postalCode): array|false {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
FROM entites
|
||||
WHERE code_postal = ? AND chk_active = 1
|
||||
');
|
||||
|
||||
$stmt->execute([$postalCode]);
|
||||
$entite = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$entite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Déchiffrement du nom
|
||||
$entite['name'] = ApiService::decryptData($entite['encrypted_name']);
|
||||
unset($entite['encrypted_name']);
|
||||
|
||||
return $entite;
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la récupération de l\'entité GeoSector par code postal', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'postalCode' => $postalCode
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si une entité existe avec le code postal spécifié, et en crée une nouvelle si nécessaire
|
||||
*
|
||||
* @param string $name Nom de l'amicale
|
||||
* @param string $postalCode Code postal
|
||||
* @return int ID de l'entité créée ou existante
|
||||
* @throws Exception Si une entité existe déjà avec ce code postal
|
||||
*/
|
||||
public function getOrCreateEntiteByPostalCode(string $name, string $postalCode): int {
|
||||
try {
|
||||
// Vérification que le code postal n'existe pas déjà
|
||||
$stmt = $this->db->prepare('SELECT COUNT(*) as count FROM entites WHERE code_postal = ?');
|
||||
$stmt->execute([$postalCode]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result && $result['count'] > 0) {
|
||||
throw new Exception('Une amicale est déjà inscrite à ce code postal');
|
||||
}
|
||||
|
||||
// Chiffrement du nom
|
||||
$encryptedName = ApiService::encryptData($name);
|
||||
|
||||
// Insertion de la nouvelle entité
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO entites (
|
||||
encrypted_name,
|
||||
code_postal,
|
||||
ville,
|
||||
fk_type,
|
||||
created_at,
|
||||
chk_active
|
||||
) VALUES (?, ?, "", 1, NOW(), 1)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
$encryptedName,
|
||||
$postalCode
|
||||
]);
|
||||
|
||||
$entiteId = (int)$this->db->lastInsertId();
|
||||
|
||||
if (!$entiteId) {
|
||||
throw new Exception('Erreur lors de la création de l\'entité');
|
||||
}
|
||||
|
||||
LogService::log('Création d\'une nouvelle entité GeoSector via getOrCreateEntiteByPostalCode', [
|
||||
'level' => 'info',
|
||||
'entiteId' => $entiteId,
|
||||
'name' => $name,
|
||||
'postalCode' => $postalCode
|
||||
]);
|
||||
|
||||
return $entiteId;
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la vérification/création de l\'entité GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'name' => $name,
|
||||
'postalCode' => $postalCode
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les entités actives
|
||||
*
|
||||
* @return array Liste des entités
|
||||
*/
|
||||
public function getEntites(): void {
|
||||
try {
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, encrypted_name, code_postal, ville, fk_type, chk_active
|
||||
FROM entites
|
||||
WHERE chk_active = 1
|
||||
ORDER BY code_postal ASC
|
||||
');
|
||||
|
||||
$stmt->execute();
|
||||
$entites = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$result = [];
|
||||
foreach ($entites as $entite) {
|
||||
// Déchiffrement du nom pour chaque entité
|
||||
$entite['name'] = ApiService::decryptData($entite['encrypted_name']);
|
||||
unset($entite['encrypted_name']);
|
||||
$result[] = $entite;
|
||||
}
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'entites' => $result
|
||||
], 200);
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la récupération des entités GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Erreur lors de la récupération des entités'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche les coordonnées GPS d'une caserne de pompiers à partir d'une adresse
|
||||
*
|
||||
* @param string $address Adresse complète (adresse + code postal + ville)
|
||||
* @param string $postalCode Code postal
|
||||
* @param string $city Ville
|
||||
* @return array|null Tableau contenant les coordonnées GPS [lat, lng] ou null si non trouvé
|
||||
*/
|
||||
private function findFireStationCoordinates(string $address, string $postalCode, string $city): ?array {
|
||||
try {
|
||||
// Construire l'adresse complète
|
||||
$fullAddress = urlencode($address . ' ' . $postalCode . ' ' . $city);
|
||||
|
||||
// Mots-clés pour rechercher une caserne de pompiers
|
||||
$keywords = ['pompiers', 'sapeurs-pompiers', 'sdis', 'caserne', 'centre de secours'];
|
||||
|
||||
foreach ($keywords as $keyword) {
|
||||
// Construire l'URL de recherche
|
||||
$searchUrl = "https://api-adresse.data.gouv.fr/search/?q=" . urlencode($keyword) . "&postcode=$postalCode&limit=1";
|
||||
|
||||
// Effectuer la requête
|
||||
$response = file_get_contents($searchUrl);
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Vérifier si des résultats ont été trouvés
|
||||
if (isset($data['features']) && count($data['features']) > 0) {
|
||||
$feature = $data['features'][0];
|
||||
|
||||
// Vérifier si les coordonnées sont disponibles
|
||||
if (isset($feature['geometry']['coordinates'])) {
|
||||
$coordinates = $feature['geometry']['coordinates'];
|
||||
|
||||
// Les coordonnées sont au format [longitude, latitude]
|
||||
return [
|
||||
'lat' => $coordinates[1],
|
||||
'lng' => $coordinates[0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucune caserne n'a été trouvée, essayer avec l'adresse de la mairie
|
||||
$searchUrl = "https://api-adresse.data.gouv.fr/search/?q=mairie&postcode=$postalCode&limit=1";
|
||||
$response = file_get_contents($searchUrl);
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data['features']) && count($data['features']) > 0) {
|
||||
$feature = $data['features'][0];
|
||||
|
||||
if (isset($feature['geometry']['coordinates'])) {
|
||||
$coordinates = $feature['geometry']['coordinates'];
|
||||
|
||||
return [
|
||||
'lat' => $coordinates[1],
|
||||
'lng' => $coordinates[0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si toujours rien, essayer avec l'adresse complète
|
||||
$searchUrl = "https://api-adresse.data.gouv.fr/search/?q=$fullAddress&limit=1";
|
||||
$response = file_get_contents($searchUrl);
|
||||
|
||||
if ($response) {
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if (isset($data['features']) && count($data['features']) > 0) {
|
||||
$feature = $data['features'][0];
|
||||
|
||||
if (isset($feature['geometry']['coordinates'])) {
|
||||
$coordinates = $feature['geometry']['coordinates'];
|
||||
|
||||
return [
|
||||
'lat' => $coordinates[1],
|
||||
'lng' => $coordinates[0]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aucune coordonnée trouvée
|
||||
return null;
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la recherche des coordonnées GPS', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'address' => $address,
|
||||
'postalCode' => $postalCode,
|
||||
'city' => $city
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour une entité existante avec les données fournies
|
||||
* Seuls les administrateurs (rôle > 2) peuvent modifier certains champs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateEntite(): void {
|
||||
try {
|
||||
// Vérifier l'authentification et les droits d'accès
|
||||
$userId = Session::getUserId();
|
||||
if (!$userId) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Vous devez être connecté pour effectuer cette action'
|
||||
], 401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupérer le rôle de l'utilisateur
|
||||
$stmt = $this->db->prepare('SELECT fk_role FROM users WHERE id = ?');
|
||||
$stmt->execute([$userId]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$user) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Utilisateur non trouvé'
|
||||
], 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$userRole = (int)$user['fk_role'];
|
||||
$isAdmin = $userRole > 2;
|
||||
|
||||
// Récupérer les données de la requête
|
||||
$data = Request::getJson();
|
||||
|
||||
if (!isset($data['id']) || empty($data['id'])) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'ID de l\'entité requis'
|
||||
], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
$entiteId = (int)$data['id'];
|
||||
|
||||
// Récupérer les données actuelles de l'entité pour vérifier si l'adresse a changé
|
||||
$stmt = $this->db->prepare('SELECT adresse1, adresse2, code_postal, ville FROM entites WHERE id = ?');
|
||||
$stmt->execute([$entiteId]);
|
||||
$currentEntite = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$currentEntite) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Entité non trouvée'
|
||||
], 404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier si l'adresse a changé
|
||||
$addressChanged = false;
|
||||
$newAdresse1 = $data['adresse1'] ?? $currentEntite['adresse1'];
|
||||
$newAdresse2 = $data['adresse2'] ?? $currentEntite['adresse2'];
|
||||
$newCodePostal = $data['code_postal'] ?? $currentEntite['code_postal'];
|
||||
$newVille = $data['ville'] ?? $currentEntite['ville'];
|
||||
|
||||
// Vérifier si l'adresse a changé
|
||||
if (
|
||||
$newAdresse1 !== $currentEntite['adresse1'] ||
|
||||
$newAdresse2 !== $currentEntite['adresse2'] ||
|
||||
$newCodePostal !== $currentEntite['code_postal'] ||
|
||||
$newVille !== $currentEntite['ville']
|
||||
) {
|
||||
$addressChanged = true;
|
||||
}
|
||||
|
||||
// Si l'adresse a changé, recalculer les coordonnées GPS
|
||||
if ($addressChanged) {
|
||||
// Construire l'adresse complète
|
||||
$fullAddress = $newAdresse1;
|
||||
if (!empty($newAdresse2)) {
|
||||
$fullAddress .= ' ' . $newAdresse2;
|
||||
}
|
||||
|
||||
// Rechercher les coordonnées GPS de la caserne de pompiers
|
||||
$coordinates = $this->findFireStationCoordinates($fullAddress, $newCodePostal, $newVille);
|
||||
|
||||
// Si des coordonnées ont été trouvées, les ajouter aux champs à mettre à jour
|
||||
if ($coordinates) {
|
||||
$data['gps_lat'] = $coordinates['lat'];
|
||||
$data['gps_lng'] = $coordinates['lng'];
|
||||
|
||||
LogService::log('Coordonnées GPS mises à jour suite à un changement d\'adresse', [
|
||||
'level' => 'info',
|
||||
'entiteId' => $entiteId,
|
||||
'lat' => $coordinates['lat'],
|
||||
'lng' => $coordinates['lng']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Préparer les champs à mettre à jour
|
||||
$updateFields = [];
|
||||
$params = [];
|
||||
|
||||
// Champs modifiables par tous les utilisateurs
|
||||
if (isset($data['name']) && !empty($data['name'])) {
|
||||
$updateFields[] = 'encrypted_name = ?';
|
||||
$params[] = ApiService::encryptData($data['name']);
|
||||
}
|
||||
|
||||
if (isset($data['adresse1'])) {
|
||||
$updateFields[] = 'adresse1 = ?';
|
||||
$params[] = $data['adresse1'];
|
||||
}
|
||||
|
||||
if (isset($data['adresse2'])) {
|
||||
$updateFields[] = 'adresse2 = ?';
|
||||
$params[] = $data['adresse2'];
|
||||
}
|
||||
|
||||
if (isset($data['code_postal']) && !empty($data['code_postal'])) {
|
||||
$updateFields[] = 'code_postal = ?';
|
||||
$params[] = $data['code_postal'];
|
||||
}
|
||||
|
||||
if (isset($data['ville'])) {
|
||||
$updateFields[] = 'ville = ?';
|
||||
$params[] = $data['ville'];
|
||||
}
|
||||
|
||||
if (isset($data['fk_region'])) {
|
||||
$updateFields[] = 'fk_region = ?';
|
||||
$params[] = $data['fk_region'];
|
||||
}
|
||||
|
||||
if (isset($data['phone'])) {
|
||||
$updateFields[] = 'encrypted_phone = ?';
|
||||
$params[] = ApiService::encryptData($data['phone']);
|
||||
}
|
||||
|
||||
if (isset($data['mobile'])) {
|
||||
$updateFields[] = 'encrypted_mobile = ?';
|
||||
$params[] = ApiService::encryptData($data['mobile']);
|
||||
}
|
||||
|
||||
if (isset($data['email']) && !empty($data['email'])) {
|
||||
$updateFields[] = 'encrypted_email = ?';
|
||||
$params[] = ApiService::encryptSearchableData($data['email']);
|
||||
}
|
||||
|
||||
if (isset($data['chk_copie_mail_recu'])) {
|
||||
$updateFields[] = 'chk_copie_mail_recu = ?';
|
||||
$params[] = $data['chk_copie_mail_recu'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (isset($data['chk_accept_sms'])) {
|
||||
$updateFields[] = 'chk_accept_sms = ?';
|
||||
$params[] = $data['chk_accept_sms'] ? 1 : 0;
|
||||
}
|
||||
|
||||
// Champs modifiables uniquement par les administrateurs
|
||||
if ($isAdmin) {
|
||||
if (isset($data['gps_lat'])) {
|
||||
$updateFields[] = 'gps_lat = ?';
|
||||
$params[] = $data['gps_lat'];
|
||||
}
|
||||
|
||||
if (isset($data['gps_lng'])) {
|
||||
$updateFields[] = 'gps_lng = ?';
|
||||
$params[] = $data['gps_lng'];
|
||||
}
|
||||
|
||||
if (isset($data['stripe_id'])) {
|
||||
$updateFields[] = 'encrypted_stripe_id = ?';
|
||||
$params[] = ApiService::encryptData($data['stripe_id']);
|
||||
}
|
||||
|
||||
if (isset($data['chk_demo'])) {
|
||||
$updateFields[] = 'chk_demo = ?';
|
||||
$params[] = $data['chk_demo'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (isset($data['chk_active'])) {
|
||||
$updateFields[] = 'chk_active = ?';
|
||||
$params[] = $data['chk_active'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (isset($data['chk_stripe'])) {
|
||||
$updateFields[] = 'chk_stripe = ?';
|
||||
$params[] = $data['chk_stripe'] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucun champ à mettre à jour, retourner une erreur
|
||||
if (empty($updateFields)) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Aucune donnée à mettre à jour'
|
||||
], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ajouter la date de mise à jour
|
||||
$updateFields[] = 'updated_at = NOW()';
|
||||
|
||||
// Construire la requête SQL
|
||||
$sql = 'UPDATE entites SET ' . implode(', ', $updateFields) . ' WHERE id = ?';
|
||||
$params[] = $entiteId;
|
||||
|
||||
// Exécuter la requête
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
|
||||
// Vérifier si la mise à jour a réussi
|
||||
if ($stmt->rowCount() === 0) {
|
||||
Response::json([
|
||||
'status' => 'warning',
|
||||
'message' => 'Aucune modification effectuée'
|
||||
], 200);
|
||||
return;
|
||||
}
|
||||
|
||||
LogService::log('Mise à jour d\'une entité GeoSector', [
|
||||
'level' => 'info',
|
||||
'userId' => $userId,
|
||||
'entiteId' => $entiteId,
|
||||
'isAdmin' => $isAdmin
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Entité mise à jour avec succès'
|
||||
], 200);
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la mise à jour de l\'entité GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Erreur lors de la mise à jour de l\'entité'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
api/src/Controllers/LogController.php
Normal file
161
api/src/Controllers/LogController.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
class LogController {
|
||||
private const REQUIRED_FIELDS = [
|
||||
'metadata' => [
|
||||
'side',
|
||||
'version',
|
||||
'level',
|
||||
'timestamp',
|
||||
'environment',
|
||||
'client' => [
|
||||
'ip',
|
||||
'browser' => ['name', 'version'],
|
||||
'os' => ['name', 'version'],
|
||||
'screenResolution',
|
||||
'userAgent'
|
||||
]
|
||||
],
|
||||
'message'
|
||||
];
|
||||
|
||||
public function index(): void {
|
||||
try {
|
||||
// Récupérer la configuration de l'application
|
||||
$appConfig = AppConfig::getInstance();
|
||||
$appName = $appConfig->getName();
|
||||
$clientType = ClientDetector::getClientType();
|
||||
|
||||
// Récupérer et décoder le JSON
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Vérifier si le JSON est valide
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new \Exception('Invalid JSON format');
|
||||
}
|
||||
|
||||
// Valider la structure des données
|
||||
$this->validateData($data);
|
||||
|
||||
// Ajouter le type de client aux métadonnées
|
||||
$data['metadata']['client_type'] = $clientType;
|
||||
|
||||
// Si c'est une app mobile, ajouter l'identifiant de l'app
|
||||
if ($clientType === 'mobile') {
|
||||
$data['metadata']['app_identifier'] = ClientDetector::getAppIdentifier();
|
||||
}
|
||||
|
||||
|
||||
// Définir le chemin du dossier logs à la racine du projet
|
||||
$logDir = __DIR__ . '/../../logs';
|
||||
|
||||
// Créer le dossier logs s'il n'existe pas
|
||||
if (!is_dir($logDir)) {
|
||||
if (!mkdir($logDir, 0777, true)) {
|
||||
throw new \Exception("Impossible de créer le dossier de logs: {$logDir}");
|
||||
}
|
||||
// S'assurer que les permissions sont correctes
|
||||
chmod($logDir, 0777);
|
||||
}
|
||||
|
||||
// Vérifier si le dossier est accessible en écriture
|
||||
if (!is_writable($logDir)) {
|
||||
throw new \Exception("Le dossier de logs n'est pas accessible en écriture: {$logDir}");
|
||||
}
|
||||
|
||||
// Récupérer l'environnement défini dans la configuration
|
||||
$environment = $appConfig->getEnvironment();
|
||||
|
||||
// Créer le nom du fichier basé sur l'environnement et la date
|
||||
// Format: geosector-production-2025-03-28.log, geosector-recette-2025-03-28.log, geosector-development-2025-03-28.log
|
||||
$filename = $logDir . '/geosector-' . $environment . '-' . date('Y-m-d') . '.log';
|
||||
|
||||
// Formater la ligne de log au format plat demandé
|
||||
// timestamp;browser.name@browser.version;os.name@os.version;client_type;$metadata;$message
|
||||
$timestamp = date('Y-m-d\TH:i:s');
|
||||
$browserInfo = $data['metadata']['client']['browser']['name'] . '@' . $data['metadata']['client']['browser']['version'];
|
||||
$osInfo = $data['metadata']['client']['os']['name'] . '@' . $data['metadata']['client']['os']['version'];
|
||||
$clientType = $data['metadata']['client_type'];
|
||||
|
||||
// Extraire le niveau de log
|
||||
$level = isset($data['metadata']['level']) ? (is_array($data['metadata']['level']) ? 'info' : $data['metadata']['level']) : 'info';
|
||||
|
||||
// Préparer les métadonnées supplémentaires (exclure celles déjà incluses dans le format et les informations client)
|
||||
$additionalMetadata = [];
|
||||
foreach ($data['metadata'] as $key => $value) {
|
||||
// Exclure les informations client, type client, side, version, level et environment
|
||||
if (!in_array($key, ['client', 'client_type', 'side', 'version', 'level', 'environment'])) {
|
||||
if (is_array($value)) {
|
||||
$additionalMetadata[$key] = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
$additionalMetadata[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Joindre les métadonnées supplémentaires avec des virgules
|
||||
$metadataStr = !empty($additionalMetadata) ? implode(',', array_map(function($k, $v) {
|
||||
return $k . '=' . $v;
|
||||
}, array_keys($additionalMetadata), $additionalMetadata)) : '-';
|
||||
|
||||
// Construire la ligne de log au format demandé
|
||||
$logLine = implode(';', [
|
||||
$timestamp,
|
||||
$browserInfo,
|
||||
$osInfo,
|
||||
$clientType,
|
||||
$level,
|
||||
$metadataStr,
|
||||
$data['message']
|
||||
]) . "\n";
|
||||
|
||||
// Écrire dans le fichier avec vérification complète
|
||||
if (file_put_contents($filename, $logLine, FILE_APPEND) === false) {
|
||||
throw new \Exception("Impossible d'écrire dans le fichier de logs: {$filename}");
|
||||
}
|
||||
|
||||
// Retourner 204 No Content en cas de succès
|
||||
http_response_code(204);
|
||||
} catch (\Exception $e) {
|
||||
Response::json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateData($data): void {
|
||||
if (!isset($data['metadata']) || !isset($data['message'])) {
|
||||
throw new \Exception('Missing required root fields');
|
||||
}
|
||||
|
||||
// Valider la structure metadata
|
||||
foreach (self::REQUIRED_FIELDS['metadata'] as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (!isset($data['metadata'][$key])) {
|
||||
throw new \Exception("Missing metadata field: {$key}");
|
||||
}
|
||||
foreach ($value as $subKey => $subValue) {
|
||||
if (is_array($subValue)) {
|
||||
foreach ($subValue as $field) {
|
||||
if (!isset($data['metadata'][$key][$subKey][$field])) {
|
||||
throw new \Exception("Missing metadata field: {$key}.{$subKey}.{$field}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isset($data['metadata'][$key][$subValue])) {
|
||||
throw new \Exception("Missing metadata field: {$key}.{$subValue}");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!isset($data['metadata'][$value])) {
|
||||
throw new \Exception("Missing metadata field: {$value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1276
api/src/Controllers/LoginController.php
Normal file
1276
api/src/Controllers/LoginController.php
Normal file
File diff suppressed because it is too large
Load Diff
605
api/src/Controllers/UserController.php
Normal file
605
api/src/Controllers/UserController.php
Normal file
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
require_once __DIR__ . '/../Services/LogService.php';
|
||||
require_once __DIR__ . '/../Services/ApiService.php';
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Database;
|
||||
use AppConfig;
|
||||
use Request;
|
||||
use Response;
|
||||
use Session;
|
||||
use LogService;
|
||||
use ApiService;
|
||||
|
||||
class UserController {
|
||||
private PDO $db;
|
||||
private AppConfig $appConfig;
|
||||
|
||||
public function __construct() {
|
||||
$this->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.encrypt_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['encrypt_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['encrypt_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.encrypt_email,
|
||||
u.encrypted_name,
|
||||
u.first_name,
|
||||
u.sect_name,
|
||||
u.encrypt_phone,
|
||||
u.encrypt_mobile,
|
||||
u.fk_role as role,
|
||||
u.fk_entite,
|
||||
u.infos,
|
||||
u.chk_alert_email,
|
||||
u.chk_suivi,
|
||||
u.date_naissance,
|
||||
u.date_embauche,
|
||||
u.matricule,
|
||||
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['encrypt_email']);
|
||||
$user['name'] = ApiService::decryptData($user['encrypted_name']);
|
||||
$user['phone'] = ApiService::decryptData($user['encrypt_phone'] ?? '');
|
||||
$user['mobile'] = ApiService::decryptData($user['encrypt_mobile'] ?? '');
|
||||
|
||||
if (!empty($user['entite_name'])) {
|
||||
$user['entite_name'] = ApiService::decryptData($user['entite_name']);
|
||||
}
|
||||
|
||||
// Suppression des champs chiffrés
|
||||
unset($user['encrypt_email']);
|
||||
unset($user['encrypted_name']);
|
||||
unset($user['encrypt_phone']);
|
||||
unset($user['encrypt_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']) ? trim($data['role']) : '1';
|
||||
$entiteId = isset($data['fk_entite']) ? (int)$data['fk_entite'] : 1;
|
||||
|
||||
// Vérification des longueurs d'entrée
|
||||
if (strlen($email) > 255 || strlen($name) > 255) {
|
||||
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 encrypt_email = ?');
|
||||
$checkStmt->execute([$encryptedEmail]);
|
||||
if ($checkStmt->fetch()) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Cet email est déjà utilisé'
|
||||
], 409);
|
||||
return;
|
||||
}
|
||||
|
||||
// Génération 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']) : '';
|
||||
$infos = isset($data['infos']) ? trim($data['infos']) : '';
|
||||
$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;
|
||||
$matricule = isset($data['matricule']) ? trim($data['matricule']) : '';
|
||||
|
||||
// Insertion en base de données
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO users (
|
||||
encrypt_email, user_pswd, encrypted_name, first_name,
|
||||
sect_name, encrypt_phone, encrypt_mobile, fk_role,
|
||||
fk_entite, infos, chk_alert_email, chk_suivi,
|
||||
date_naissance, date_embauche, matricule,
|
||||
created_at, fk_user_creat, chk_active
|
||||
) VALUES (
|
||||
?, ?, ?, ?,
|
||||
?, ?, ?, ?,
|
||||
?, ?, ?, ?,
|
||||
?, ?, ?,
|
||||
NOW(), ?, 1
|
||||
)
|
||||
');
|
||||
$stmt->execute([
|
||||
$encryptedEmail,
|
||||
$passwordHash,
|
||||
$encryptedName,
|
||||
$firstName,
|
||||
$sectName,
|
||||
$phone,
|
||||
$mobile,
|
||||
$role,
|
||||
$entiteId,
|
||||
$infos,
|
||||
$alertEmail,
|
||||
$suivi,
|
||||
$dateNaissance,
|
||||
$dateEmbauche,
|
||||
$matricule,
|
||||
$currentUserId
|
||||
]);
|
||||
$userId = $this->db->lastInsertId();
|
||||
|
||||
// Envoi de l'email avec les identifiants
|
||||
ApiService::sendEmail($email, $name, 'welcome', ['password' => $password]);
|
||||
|
||||
LogService::log('Utilisateur GeoSector créé', [
|
||||
'level' => 'info',
|
||||
'createdBy' => $currentUserId,
|
||||
'newUserId' => $userId,
|
||||
'email' => $email
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Utilisateur créé avec succès',
|
||||
'id' => $userId
|
||||
], 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 encrypt_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[] = "encrypt_email = :encrypt_email";
|
||||
$params['encrypt_email'] = $encryptedEmail;
|
||||
}
|
||||
|
||||
if (isset($data['name'])) {
|
||||
$updateFields[] = "encrypted_name = :encrypted_name";
|
||||
$params['encrypted_name'] = ApiService::encryptData(trim($data['name']));
|
||||
}
|
||||
|
||||
if (isset($data['phone'])) {
|
||||
$updateFields[] = "encrypt_phone = :encrypt_phone";
|
||||
$params['encrypt_phone'] = ApiService::encryptData(trim($data['phone']));
|
||||
}
|
||||
|
||||
if (isset($data['mobile'])) {
|
||||
$updateFields[] = "encrypt_mobile = :encrypt_mobile";
|
||||
$params['encrypt_mobile'] = ApiService::encryptData(trim($data['mobile']));
|
||||
}
|
||||
|
||||
// Traitement des champs non chiffrés
|
||||
$nonEncryptedFields = [
|
||||
'first_name',
|
||||
'sect_name',
|
||||
'fk_role',
|
||||
'fk_entite',
|
||||
'infos',
|
||||
'chk_alert_email',
|
||||
'chk_suivi',
|
||||
'date_naissance',
|
||||
'date_embauche',
|
||||
'matricule',
|
||||
'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_pswd = :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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
$currentUserId = Session::getUserId();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
try {
|
||||
// Désactivation de l'utilisateur plutôt que suppression
|
||||
$stmt = $this->db->prepare('
|
||||
UPDATE users
|
||||
SET chk_active = 0,
|
||||
updated_at = NOW(),
|
||||
fk_user_modif = ?
|
||||
WHERE id = ?
|
||||
');
|
||||
$stmt->execute([$currentUserId, $id]);
|
||||
|
||||
if ($stmt->rowCount() === 0) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Utilisateur non trouvé'
|
||||
], 404);
|
||||
return;
|
||||
}
|
||||
|
||||
LogService::log('Utilisateur GeoSector désactivé', [
|
||||
'level' => 'info',
|
||||
'deactivatedBy' => $currentUserId,
|
||||
'userId' => $id
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'message' => 'Utilisateur désactivé avec succès'
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
LogService::log('Erreur lors de la désactivation d\'un utilisateur GeoSector', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'userId' => $id
|
||||
]);
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Erreur serveur'
|
||||
], 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;
|
||||
}
|
||||
}
|
||||
99
api/src/Controllers/VilleController.php
Normal file
99
api/src/Controllers/VilleController.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
require_once __DIR__ . '/../Services/LogService.php';
|
||||
require_once __DIR__ . '/../Services/ApiService.php';
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Database;
|
||||
use AppConfig;
|
||||
use Request;
|
||||
use Response;
|
||||
use Session;
|
||||
use LogService;
|
||||
use ApiService;
|
||||
use Exception;
|
||||
|
||||
class VilleController {
|
||||
private PDO $db;
|
||||
private AppConfig $appConfig;
|
||||
|
||||
public function __construct() {
|
||||
$this->db = Database::getInstance();
|
||||
$this->appConfig = AppConfig::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche les villes dont le code postal commence par les chiffres saisis
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function searchVillesByPostalCode(): void {
|
||||
try {
|
||||
// Récupérer le paramètre code_postal de la requête
|
||||
$postalCode = Request::getValue('code_postal');
|
||||
|
||||
if (empty($postalCode) || strlen($postalCode) < 3) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Le code postal doit contenir au moins 3 chiffres'
|
||||
], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Valider que le code postal ne contient que des chiffres
|
||||
if (!ctype_digit($postalCode)) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => 'Le code postal doit contenir uniquement des chiffres'
|
||||
], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rechercher les villes dont le code postal commence par les chiffres saisis
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT id, fk_departement, libelle, code_postal
|
||||
FROM x_villes
|
||||
WHERE code_postal LIKE ?
|
||||
ORDER BY libelle ASC
|
||||
LIMIT 20
|
||||
');
|
||||
|
||||
$stmt->execute([$postalCode . '%']);
|
||||
$villes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Renommer les champs pour une meilleure lisibilité côté client
|
||||
$result = [];
|
||||
foreach ($villes as $ville) {
|
||||
$result[] = [
|
||||
'id' => $ville['id'],
|
||||
'departement_id' => $ville['fk_departement'],
|
||||
'nom' => $ville['libelle'],
|
||||
'code_postal' => $ville['code_postal']
|
||||
];
|
||||
}
|
||||
|
||||
Response::json([
|
||||
'status' => 'success',
|
||||
'success' => true,
|
||||
'data' => $result
|
||||
], 200);
|
||||
} catch (Exception $e) {
|
||||
LogService::log('Erreur lors de la recherche de villes par code postal', [
|
||||
'level' => 'error',
|
||||
'error' => $e->getMessage(),
|
||||
'postalCode' => $postalCode ?? 'non défini'
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'success' => false,
|
||||
'message' => 'Erreur lors de la recherche de villes'
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user