- Sauvegarde des fichiers critiques - Préparation transformation ApiService en singleton - Préparation création CurrentUserService et CurrentAmicaleService - Objectif: renommer Box users -> user
1279 lines
56 KiB
PHP
1279 lines
56 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controllers;
|
|
|
|
use PHPMailer\PHPMailer\PHPMailer;
|
|
use PHPMailer\PHPMailer\SMTP;
|
|
use PHPMailer\PHPMailer\Exception;
|
|
use PDO;
|
|
use PDOException;
|
|
use Database;
|
|
use AppConfig;
|
|
use Request;
|
|
use Response;
|
|
use Session;
|
|
use LogService;
|
|
use ApiService;
|
|
|
|
require_once __DIR__ . '/../Services/LogService.php';
|
|
require_once __DIR__ . '/../Services/ApiService.php';
|
|
require_once __DIR__ . '/EntiteController.php';
|
|
|
|
class LoginController {
|
|
private PDO $db;
|
|
private AppConfig $appConfig;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
$this->appConfig = AppConfig::getInstance();
|
|
}
|
|
|
|
public function login(): void {
|
|
try {
|
|
$data = Request::getJson();
|
|
|
|
if (!isset($data['username'], $data['password'], $data['type'])) {
|
|
LogService::log('Tentative de connexion GeoSector échouée : données manquantes', [
|
|
'level' => 'warning',
|
|
'username' => $data['username'] ?? 'non fourni'
|
|
]);
|
|
Response::json(['error' => 'Nom d\'utilisateur et mot de passe requis'], 400);
|
|
return;
|
|
}
|
|
|
|
$interface = trim($data['type']);
|
|
$username = trim($data['username']);
|
|
$encryptedUsername = ApiService::encryptSearchableData($username);
|
|
|
|
// Récupérer le type d'utilisateur
|
|
// admin accessible uniquement aux fk_role>1
|
|
// sinon tout user peut se connecter à l'interface utilisateur
|
|
$roleCondition = ($interface === 'user') ? '' : 'AND fk_role>1';
|
|
|
|
// Log pour le debug
|
|
LogService::log('Tentative de connexion GeoSector', [
|
|
'level' => 'info',
|
|
'username' => $username,
|
|
'type' => $interface,
|
|
'role_condition' => $roleCondition
|
|
]);
|
|
|
|
// Requête optimisée: on récupère l'utilisateur et son entité en une seule fois avec LEFT JOIN
|
|
$stmt = $this->db->prepare(
|
|
'SELECT
|
|
u.id, u.encrypted_email, u.encrypted_user_name, u.encrypted_name, u.user_pass_hash,
|
|
u.first_name, u.fk_role, u.fk_entite, u.chk_active, u.sect_name,
|
|
e.id AS entite_id, e.encrypted_name AS entite_encrypted_name,
|
|
e.adresse1, e.code_postal, e.ville, e.gps_lat, e.gps_lng, e.chk_active AS entite_chk_active
|
|
FROM users u
|
|
LEFT JOIN entites e ON u.fk_entite = e.id
|
|
WHERE u.encrypted_user_name = ? AND u.chk_active != 0 ' . $roleCondition
|
|
);
|
|
$stmt->execute([$encryptedUsername]);
|
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$user) {
|
|
LogService::log('Tentative de connexion GeoSector échouée : utilisateur non trouvé', [
|
|
'level' => 'warning',
|
|
'username' => $username
|
|
]);
|
|
Response::json(['error' => 'Identifiants invalides'], 401);
|
|
return;
|
|
}
|
|
|
|
// Vérification du mot de passe
|
|
$passwordValid = password_verify($data['password'], $user['user_pass_hash']);
|
|
|
|
if (!$passwordValid) {
|
|
LogService::log('Tentative de connexion GeoSector échouée : mot de passe incorrect', [
|
|
'level' => 'warning',
|
|
'username' => $username
|
|
]);
|
|
Response::json(['error' => 'Identifiants invalides'], 401);
|
|
return;
|
|
}
|
|
|
|
// Vérifier si l'utilisateur a une entité et si elle est active
|
|
if (!empty($user['fk_entite']) && (!isset($user['entite_chk_active']) || $user['entite_chk_active'] != 1)) {
|
|
LogService::log('Tentative de connexion GeoSector échouée : entité non active', [
|
|
'level' => 'warning',
|
|
'username' => $username,
|
|
'entite_id' => $user['fk_entite']
|
|
]);
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Votre amicale n\'est pas activée. Veuillez contacter votre administrateur.'
|
|
], 403);
|
|
return;
|
|
}
|
|
|
|
// Mise à jour de last_login et activation du compte si nécessaire
|
|
$updateQuery = 'UPDATE users SET
|
|
updated_at = NOW()' .
|
|
($user['chk_active'] == -1 ? ', chk_active = 1' : '') .
|
|
($user['chk_active'] == 2 ? ', chk_active = 1' : '') .
|
|
' WHERE id = ?';
|
|
|
|
$updateStmt = $this->db->prepare($updateQuery);
|
|
$updateStmt->execute([$user['id']]);
|
|
|
|
// Déchiffrement du nom
|
|
$decryptedName = ApiService::decryptData($user['encrypted_name']);
|
|
|
|
// Déchiffrement de l'email si disponible
|
|
$email = '';
|
|
if (!empty($user['encrypted_email'])) {
|
|
$email = ApiService::decryptSearchableData($user['encrypted_email']);
|
|
|
|
// Si le déchiffrement échoue, renvoyer une erreur
|
|
if (empty($email)) {
|
|
LogService::log('Déchiffrement email échoué', [
|
|
'level' => 'error',
|
|
'message' => 'Déchiffrement de l\'email échoué',
|
|
'encrypted_email' => $user['encrypted_email'],
|
|
'user_id' => $user['id']
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Erreur de déchiffrement de l\'email. Exécutez le script de migration pour résoudre ce problème.',
|
|
'debug_info' => [
|
|
'encrypted_email' => $user['encrypted_email'],
|
|
'user_id' => $user['id']
|
|
]
|
|
], 500);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Création de la session
|
|
$sessionData = [
|
|
'id' => $user['id'],
|
|
'username' => $username,
|
|
'email' => $email,
|
|
'name' => $decryptedName,
|
|
'first_name' => $user['first_name'] ?? '',
|
|
'fk_role' => $user['fk_role'] ?? '0',
|
|
'fk_entite' => $user['fk_entite'] ?? '0',
|
|
// 'interface' supprimée pour se baser uniquement sur le rôle
|
|
];
|
|
Session::login($sessionData);
|
|
|
|
// Préparation des données utilisateur pour la réponse (uniquement les champs du user)
|
|
$userData = [
|
|
'id' => $user['id'],
|
|
'fk_entite' => $user['fk_entite'] ?? null,
|
|
'fk_role' => $user['fk_role'] ?? '0',
|
|
'fk_titre' => $user['fk_titre'] ?? null,
|
|
'first_name' => $user['first_name'] ?? '',
|
|
'sect_name' => $user['sect_name'] ?? '',
|
|
'date_naissance' => $user['date_naissance'] ?? null,
|
|
'date_embauche' => $user['date_embauche'] ?? null,
|
|
'username' => $username,
|
|
'name' => $decryptedName
|
|
];
|
|
|
|
// Déchiffrement du téléphone
|
|
if (!empty($user['encrypted_phone'])) {
|
|
$userData['phone'] = ApiService::decryptData($user['encrypted_phone']);
|
|
} else {
|
|
$userData['phone'] = '';
|
|
}
|
|
|
|
// Déchiffrement du mobile
|
|
if (!empty($user['encrypted_mobile'])) {
|
|
$userData['mobile'] = ApiService::decryptData($user['encrypted_mobile']);
|
|
} else {
|
|
$userData['mobile'] = '';
|
|
}
|
|
|
|
// L'email est déjà déchiffré plus haut dans le code
|
|
$userData['email'] = $email;
|
|
|
|
// Suivant l'interface et le role de l'utilisateur, on lui charge toutes ses données utiles
|
|
|
|
// operations :
|
|
// Si $interface='user' : on ne récupère que la dernière opération active
|
|
// Si $interface='admin' et si $user['fk_role']=2 : on récupère les 3 dernières opérations dont celle active
|
|
// Dans tous les autres cas, operations: []
|
|
|
|
// secteurs :
|
|
// On récupère les secteurs de l'opération active trouvée, sinon secteurs: []
|
|
|
|
// passages :
|
|
// On récupère les passages du ou des secteurs trouvés, sinon passages: []
|
|
|
|
// users_sectors :
|
|
// On récupère les users affectés aux secteurs partagés de l'utilisateur, si pas de secteurs, users_passages: []
|
|
|
|
// clients :
|
|
// Si $interface="admin" et si $user['fk_role']=9
|
|
// On récupère les entités au complet sauf la entite.id=1 dans un group clients contenant id, name, adresse1, adresse2, code_postal, ville, fk_region, lib_region, fk_type, phone, mobile, email, gps_lat, gps_lng, chk_active
|
|
|
|
// Suivant l'interface et le role de l'utilisateur, on lui charge toutes ses données utiles
|
|
$operationsData = [];
|
|
$sectorsData = [];
|
|
$passagesData = [];
|
|
$usersSectorsData = [];
|
|
|
|
// 1. Récupération des opérations selon les critères
|
|
$operationLimit = 0;
|
|
$activeOperationOnly = false;
|
|
|
|
if ($interface === 'user') {
|
|
// Interface utilisateur : seulement la dernière opération active
|
|
$operationLimit = 1;
|
|
$activeOperationOnly = true;
|
|
} elseif ($interface === 'admin' && $user['fk_role'] == 2) {
|
|
// Interface admin avec rôle 2 : les 3 dernières opérations dont l'active
|
|
$operationLimit = 3;
|
|
} else {
|
|
// Autres cas : pas d'opérations
|
|
$operationLimit = 0;
|
|
}
|
|
|
|
if ($operationLimit > 0 && !empty($user['fk_entite'])) {
|
|
$operationQuery = "SELECT id, libelle, date_deb, date_fin
|
|
FROM operations
|
|
WHERE fk_entite = ?";
|
|
|
|
if ($activeOperationOnly) {
|
|
$operationQuery .= " AND chk_active = 1";
|
|
}
|
|
|
|
$operationQuery .= " ORDER BY id DESC LIMIT " . $operationLimit;
|
|
|
|
$operationStmt = $this->db->prepare($operationQuery);
|
|
$operationStmt->execute([$user['fk_entite']]);
|
|
$operations = $operationStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($operations)) {
|
|
// Formater les données des opérations
|
|
foreach ($operations as $operation) {
|
|
$operationsData[] = [
|
|
'id' => $operation['id'],
|
|
'name' => $operation['libelle'],
|
|
'date_deb' => $operation['date_deb'],
|
|
'date_fin' => $operation['date_fin']
|
|
];
|
|
}
|
|
|
|
// Récupérer l'ID de l'opération active (première opération retournée)
|
|
$activeOperationId = $operations[0]['id'];
|
|
|
|
// 2. Récupérer les secteurs selon l'interface et le rôle
|
|
if ($interface === 'user') {
|
|
// Interface utilisateur : seulement les secteurs affectés à l'utilisateur
|
|
$sectorsStmt = $this->db->prepare(
|
|
'SELECT s.id, s.libelle, s.color, s.sector
|
|
FROM ope_sectors s
|
|
JOIN ope_users_sectors us ON s.id = us.fk_sector
|
|
WHERE us.fk_operation = ? AND us.fk_user = ? AND us.chk_active = 1 AND s.chk_active = 1'
|
|
);
|
|
$sectorsStmt->execute([$activeOperationId, $user['id']]);
|
|
} elseif ($interface === 'admin' && $user['fk_role'] == 2) {
|
|
// Interface admin avec rôle 2 : tous les secteurs distincts de l'opération
|
|
$sectorsStmt = $this->db->prepare(
|
|
'SELECT DISTINCT s.id, s.libelle, s.color, s.sector
|
|
FROM ope_sectors s
|
|
WHERE s.fk_operation = ? AND s.chk_active = 1'
|
|
);
|
|
$sectorsStmt->execute([$activeOperationId]);
|
|
} else {
|
|
// Autres cas : pas de secteurs
|
|
$sectors = [];
|
|
$sectorsData = [];
|
|
}
|
|
|
|
// Récupération des secteurs si une requête a été préparée
|
|
if (isset($sectorsStmt)) {
|
|
$sectors = $sectorsStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} else {
|
|
$sectors = [];
|
|
}
|
|
|
|
if (!empty($sectors)) {
|
|
$sectorsData = $sectors;
|
|
|
|
// 3. Récupérer les passages selon l'interface et le rôle
|
|
if ($interface === 'user' && !empty($sectors)) {
|
|
// Interface utilisateur : passages liés aux secteurs de l'utilisateur
|
|
$sectorIds = array_column($sectors, 'id');
|
|
$sectorIdsString = implode(',', $sectorIds);
|
|
|
|
if (!empty($sectorIdsString)) {
|
|
$passagesStmt = $this->db->prepare(
|
|
"SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau,
|
|
gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages
|
|
FROM ope_pass
|
|
WHERE fk_operation = ? AND fk_sector IN ($sectorIdsString) AND chk_active = 1"
|
|
);
|
|
$passagesStmt->execute([$activeOperationId]);
|
|
}
|
|
} elseif ($interface === 'admin' && $user['fk_role'] == 2) {
|
|
// Interface admin avec rôle 2 : tous les passages de l'opération
|
|
$passagesStmt = $this->db->prepare(
|
|
"SELECT id, fk_operation, fk_sector, fk_user, fk_type, fk_adresse, passed_at, numero, rue, rue_bis, ville, residence, fk_habitat, appt, niveau,
|
|
gps_lat, gps_lng, nom_recu, encrypted_name, remarque, encrypted_email, encrypted_phone, montant, fk_type_reglement, email_erreur, nb_passages
|
|
FROM ope_pass
|
|
WHERE fk_operation = ? AND chk_active = 1"
|
|
);
|
|
$passagesStmt->execute([$activeOperationId]);
|
|
} else {
|
|
// Autres cas : pas de passages
|
|
$passages = [];
|
|
$passagesData = [];
|
|
}
|
|
|
|
// Récupération des passages si une requête a été préparée
|
|
if (isset($passagesStmt)) {
|
|
$passages = $passagesStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} else {
|
|
$passages = [];
|
|
}
|
|
|
|
if (!empty($passages)) {
|
|
// Déchiffrer les données sensibles
|
|
foreach ($passages as &$passage) {
|
|
// Déchiffrement du nom
|
|
$passage['name'] = '';
|
|
if (!empty($passage['encrypted_name'])) {
|
|
$passage['name'] = ApiService::decryptData($passage['encrypted_name']);
|
|
}
|
|
unset($passage['encrypted_name']);
|
|
|
|
// Déchiffrement de l'email
|
|
$passage['email'] = '';
|
|
if (!empty($passage['encrypted_email'])) {
|
|
$decryptedEmail = ApiService::decryptSearchableData($passage['encrypted_email']);
|
|
if ($decryptedEmail) {
|
|
$passage['email'] = $decryptedEmail;
|
|
}
|
|
}
|
|
unset($passage['encrypted_email']);
|
|
|
|
// Déchiffrement du téléphone
|
|
$passage['phone'] = '';
|
|
if (!empty($passage['encrypted_phone'])) {
|
|
$passage['phone'] = ApiService::decryptData($passage['encrypted_phone']);
|
|
}
|
|
unset($passage['encrypted_phone']);
|
|
}
|
|
$passagesData = $passages;
|
|
}
|
|
|
|
// 4. Récupérer les utilisateurs des secteurs partagés
|
|
if (($interface === 'user' || ($interface === 'admin' && $user['fk_role'] == 2)) && !empty($sectors)) {
|
|
$sectorIds = array_column($sectors, 'id');
|
|
$sectorIdsString = implode(',', $sectorIds);
|
|
|
|
if (!empty($sectorIdsString)) {
|
|
$usersSectorsStmt = $this->db->prepare(
|
|
"SELECT DISTINCT u.id, u.first_name, u.encrypted_name, u.sect_name, us.fk_sector
|
|
FROM users u
|
|
JOIN ope_users_sectors us ON u.id = us.fk_user
|
|
WHERE us.fk_sector IN ($sectorIdsString)
|
|
AND us.fk_operation = ?
|
|
AND us.chk_active = 1
|
|
AND u.chk_active = 1
|
|
AND u.id != ?" // Exclure l'utilisateur connecté
|
|
);
|
|
$usersSectorsStmt->execute([$activeOperationId, $user['id']]);
|
|
$usersSectors = $usersSectorsStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($usersSectors)) {
|
|
// Déchiffrer les noms des utilisateurs
|
|
foreach ($usersSectors as &$userSector) {
|
|
if (!empty($userSector['encrypted_name'])) {
|
|
$userSector['name'] = ApiService::decryptData($userSector['encrypted_name']);
|
|
unset($userSector['encrypted_name']);
|
|
}
|
|
}
|
|
$usersSectorsData = $usersSectors;
|
|
}
|
|
}
|
|
} else {
|
|
// Autres cas : pas d'utilisateurs de secteurs
|
|
$usersSectorsData = [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Section "clients" supprimée car redondante avec le nouveau groupe "amicales"
|
|
|
|
// 6. Récupérer les membres (users de l'entité du user) si nécessaire
|
|
if ($interface === 'admin' && $user['fk_role'] == 2 && !empty($user['fk_entite'])) {
|
|
$membresStmt = $this->db->prepare(
|
|
'SELECT id, fk_role, fk_entite, fk_titre, encrypted_name, first_name, sect_name,
|
|
encrypted_user_name, encrypted_phone, encrypted_mobile, encrypted_email,
|
|
date_naissance, date_embauche, chk_active
|
|
FROM users
|
|
WHERE fk_entite = ?'
|
|
);
|
|
$membresStmt->execute([$user['fk_entite']]);
|
|
$membres = $membresStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($membres)) {
|
|
$membresData = [];
|
|
|
|
foreach ($membres as $membre) {
|
|
$membreItem = [
|
|
'id' => $membre['id'],
|
|
'fk_role' => $membre['fk_role'],
|
|
'fk_entite' => $membre['fk_entite'],
|
|
'fk_titre' => $membre['fk_titre'],
|
|
'first_name' => $membre['first_name'] ?? '',
|
|
'sect_name' => $membre['sect_name'] ?? '',
|
|
'date_naissance' => $membre['date_naissance'] ?? null,
|
|
'date_embauche' => $membre['date_embauche'] ?? null,
|
|
'chk_active' => $membre['chk_active']
|
|
];
|
|
|
|
// Déchiffrement du nom
|
|
if (!empty($membre['encrypted_name'])) {
|
|
$membreItem['name'] = ApiService::decryptData($membre['encrypted_name']);
|
|
}
|
|
|
|
// Déchiffrement du nom d'utilisateur
|
|
if (!empty($membre['encrypted_user_name'])) {
|
|
$membreItem['username'] = ApiService::decryptSearchableData($membre['encrypted_user_name']);
|
|
}
|
|
|
|
// Déchiffrement du téléphone
|
|
if (!empty($membre['encrypted_phone'])) {
|
|
$membreItem['phone'] = ApiService::decryptData($membre['encrypted_phone']);
|
|
}
|
|
|
|
// Déchiffrement du mobile
|
|
if (!empty($membre['encrypted_mobile'])) {
|
|
$membreItem['mobile'] = ApiService::decryptData($membre['encrypted_mobile']);
|
|
}
|
|
|
|
// Déchiffrement de l'email
|
|
if (!empty($membre['encrypted_email'])) {
|
|
$decryptedEmail = ApiService::decryptSearchableData($membre['encrypted_email']);
|
|
if ($decryptedEmail) {
|
|
$membreItem['email'] = $decryptedEmail;
|
|
}
|
|
}
|
|
|
|
$membresData[] = $membreItem;
|
|
}
|
|
|
|
// Les membres seront ajoutés à la racine de la réponse plus tard
|
|
// (après la préparation de la réponse)
|
|
}
|
|
}
|
|
|
|
// 7. Récupérer les amicales selon le rôle de l'utilisateur
|
|
$amicalesData = [];
|
|
|
|
if (!empty($user['fk_entite'])) {
|
|
if ($user['fk_role'] <= 2) {
|
|
// User normal ou admin avec fk_role=2: uniquement son amicale
|
|
$amicaleStmt = $this->db->prepare(
|
|
'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
|
|
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
|
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
|
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
|
FROM entites e
|
|
LEFT JOIN x_regions r ON e.fk_region = r.id
|
|
WHERE e.id = ? AND e.chk_active = 1'
|
|
);
|
|
$amicaleStmt->execute([$user['fk_entite']]);
|
|
$amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
} else {
|
|
// Admin avec fk_role>2: toutes les amicales sauf id=1
|
|
$amicaleStmt = $this->db->prepare(
|
|
'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
|
|
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
|
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
|
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
|
FROM entites e
|
|
LEFT JOIN x_regions r ON e.fk_region = r.id
|
|
WHERE e.id != 1 AND e.chk_active = 1'
|
|
);
|
|
$amicaleStmt->execute();
|
|
$amicales = $amicaleStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
if (!empty($amicales)) {
|
|
foreach ($amicales as &$amicale) {
|
|
// Déchiffrement du nom
|
|
if (!empty($amicale['name'])) {
|
|
$amicale['name'] = ApiService::decryptData($amicale['name']);
|
|
}
|
|
|
|
// Déchiffrement de l'email si disponible
|
|
if (!empty($amicale['email'])) {
|
|
$decryptedEmail = ApiService::decryptSearchableData($amicale['email']);
|
|
if ($decryptedEmail) {
|
|
$amicale['email'] = $decryptedEmail;
|
|
}
|
|
}
|
|
|
|
// Déchiffrement du téléphone
|
|
if (!empty($amicale['phone'])) {
|
|
$amicale['phone'] = ApiService::decryptData($amicale['phone']);
|
|
}
|
|
|
|
// Déchiffrement du mobile
|
|
if (!empty($amicale['mobile'])) {
|
|
$amicale['mobile'] = ApiService::decryptData($amicale['mobile']);
|
|
}
|
|
|
|
// Déchiffrement du stripe_id
|
|
if (!empty($amicale['stripe_id'])) {
|
|
$amicale['stripe_id'] = ApiService::decryptData($amicale['stripe_id']);
|
|
}
|
|
}
|
|
$amicalesData = $amicales;
|
|
}
|
|
}
|
|
|
|
// 8. Récupérer les entités de type 1 pour les utilisateurs avec fk_role > 2
|
|
$entitesData = [];
|
|
|
|
if ($user['fk_role'] > 2) {
|
|
// Admin avec fk_role > 2: toutes les entités de type 1
|
|
$entitesStmt = $this->db->prepare(
|
|
'SELECT e.id, e.encrypted_name as name, e.adresse1, e.adresse2, e.code_postal, e.ville,
|
|
e.fk_region, r.libelle AS lib_region, e.fk_type, e.encrypted_phone as phone, e.encrypted_mobile as mobile,
|
|
e.encrypted_email as email, e.gps_lat, e.gps_lng,
|
|
e.encrypted_stripe_id as stripe_id, e.chk_demo, e.chk_copie_mail_recu, e.chk_accept_sms, e.chk_active, e.chk_stripe
|
|
FROM entites e
|
|
LEFT JOIN x_regions r ON e.fk_region = r.id
|
|
WHERE e.fk_type = 1 AND e.chk_active = 1'
|
|
);
|
|
$entitesStmt->execute();
|
|
$entites = $entitesStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($entites)) {
|
|
foreach ($entites as &$entite) {
|
|
// Déchiffrement du nom
|
|
if (!empty($entite['name'])) {
|
|
$entite['name'] = ApiService::decryptData($entite['name']);
|
|
}
|
|
|
|
// Déchiffrement de l'email si disponible
|
|
if (!empty($entite['email'])) {
|
|
$decryptedEmail = ApiService::decryptSearchableData($entite['email']);
|
|
if ($decryptedEmail) {
|
|
$entite['email'] = $decryptedEmail;
|
|
}
|
|
}
|
|
|
|
// Déchiffrement du téléphone
|
|
if (!empty($entite['phone'])) {
|
|
$entite['phone'] = ApiService::decryptData($entite['phone']);
|
|
}
|
|
|
|
// Déchiffrement du mobile
|
|
if (!empty($entite['mobile'])) {
|
|
$entite['mobile'] = ApiService::decryptData($entite['mobile']);
|
|
}
|
|
|
|
// Déchiffrement du stripe_id
|
|
if (!empty($entite['stripe_id'])) {
|
|
$entite['stripe_id'] = ApiService::decryptData($entite['stripe_id']);
|
|
}
|
|
}
|
|
$entitesData = $entites;
|
|
}
|
|
}
|
|
|
|
// Préparation de la réponse
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'Connexion réussie',
|
|
'session_id' => session_id(),
|
|
'session_expiry' => date('c', strtotime('+24 hours')), // Ajoute une expiration de 24h
|
|
'user' => $userData
|
|
];
|
|
|
|
// Ajout des amicales à la racine de la réponse si disponibles
|
|
if (!empty($amicalesData)) {
|
|
// Si c'est un tableau avec un seul élément, on envoie directement l'objet
|
|
// pour que le client reçoive un objet et non un tableau avec un seul objet
|
|
if (count($amicalesData) === 1) {
|
|
$response['amicale'] = $amicalesData[0];
|
|
} else {
|
|
$response['amicale'] = $amicalesData;
|
|
}
|
|
}
|
|
|
|
// Ajout des entités à la racine de la réponse sous le nom "clients" (vide pour fk_role <= 2)
|
|
$response['clients'] = $entitesData;
|
|
|
|
// Ajout des membres à la racine de la réponse si disponibles
|
|
if (!empty($membresData)) {
|
|
$response['membres'] = $membresData;
|
|
}
|
|
|
|
// Ajout des opérations à la racine de la réponse si disponibles
|
|
if (!empty($operationsData)) {
|
|
$response['operations'] = $operationsData;
|
|
}
|
|
|
|
// Ajout des secteurs à la racine de la réponse si disponibles
|
|
if (!empty($sectorsData)) {
|
|
$response['sectors'] = $sectorsData;
|
|
}
|
|
|
|
// Ajout des passages à la racine de la réponse si disponibles
|
|
if (!empty($passagesData)) {
|
|
$response['passages'] = $passagesData;
|
|
}
|
|
|
|
// Ajout des utilisateurs des secteurs à la racine de la réponse si disponibles
|
|
if (!empty($usersSectorsData)) {
|
|
$response['users_sectors'] = $usersSectorsData;
|
|
}
|
|
|
|
// Section "clients" supprimée car redondante avec le nouveau groupe "amicales"
|
|
|
|
// 9. Récupérer les régions selon le rôle de l'utilisateur
|
|
$regionsData = [];
|
|
|
|
if ($user['fk_role'] <= 2 && !empty($user['fk_entite'])) {
|
|
// User normal ou admin avec fk_role=2: uniquement sa région basée sur le code postal de son amicale
|
|
$amicaleStmt = $this->db->prepare('SELECT code_postal FROM entites WHERE id = ?');
|
|
$amicaleStmt->execute([$user['fk_entite']]);
|
|
$amicale = $amicaleStmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($amicale) && !empty($amicale['code_postal'])) {
|
|
$departement = substr($amicale['code_postal'], 0, 2);
|
|
|
|
$regionStmt = $this->db->prepare(
|
|
'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active
|
|
FROM x_regions
|
|
WHERE FIND_IN_SET(?, departements) > 0 AND chk_active = 1'
|
|
);
|
|
$regionStmt->execute([$departement]);
|
|
$regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($regions)) {
|
|
$regionsData = $regions;
|
|
}
|
|
}
|
|
} else {
|
|
// Admin avec fk_role>2: toutes les régions
|
|
$regionStmt = $this->db->prepare(
|
|
'SELECT id, fk_pays, libelle, libelle_long, table_osm, departements, chk_active
|
|
FROM x_regions
|
|
WHERE chk_active = 1'
|
|
);
|
|
$regionStmt->execute();
|
|
$regions = $regionStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
if (!empty($regions)) {
|
|
$regionsData = $regions;
|
|
}
|
|
}
|
|
|
|
// Ajout des régions à la racine de la réponse si disponibles
|
|
if (!empty($regionsData)) {
|
|
$response['regions'] = $regionsData;
|
|
}
|
|
|
|
// Envoi de la réponse
|
|
Response::json($response);
|
|
} catch (PDOException $e) {
|
|
LogService::log('Erreur base de données lors de la connexion GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage(),
|
|
'code' => $e->getCode()
|
|
]);
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Erreur serveur'
|
|
], 500);
|
|
} catch (Exception $e) {
|
|
LogService::log('Erreur inattendue lors de la connexion GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage()
|
|
]);
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Une erreur inattendue est survenue'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function lostPassword(): void {
|
|
try {
|
|
$data = Request::getJson();
|
|
|
|
if (!isset($data['email']) || empty($data['email'])) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Email requis'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
$email = trim($data['email']);
|
|
|
|
// Validation de l'email
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Format d\'email invalide'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
// Chiffrement de l'email pour la recherche
|
|
$encryptedEmail = ApiService::encryptSearchableData($email);
|
|
|
|
// Recherche de l'utilisateur
|
|
$stmt = $this->db->prepare('
|
|
SELECT id, encrypted_name, encrypted_user_name, chk_active
|
|
FROM users
|
|
WHERE encrypted_email = ?
|
|
');
|
|
$stmt->execute([$encryptedEmail]);
|
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$user) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Aucun compte trouvé avec cet email'
|
|
], 404);
|
|
return;
|
|
}
|
|
|
|
if ($user['chk_active'] == 0) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Ce compte est désactivé. Contactez l\'administrateur.'
|
|
], 403);
|
|
return;
|
|
}
|
|
|
|
// Déchiffrement du nom et du username
|
|
$name = ApiService::decryptData($user['encrypted_name']);
|
|
$username = ApiService::decryptSearchableData($user['encrypted_user_name']);
|
|
|
|
// Génération d'un nouveau mot de passe
|
|
$newPassword = ApiService::generateSecurePassword();
|
|
$passwordHash = password_hash($newPassword, PASSWORD_DEFAULT);
|
|
|
|
// Mise à jour du mot de passe
|
|
$updateStmt = $this->db->prepare('
|
|
UPDATE users
|
|
SET user_pass_hash = ?, updated_at = NOW()
|
|
WHERE id = ?
|
|
');
|
|
$updateStmt->execute([$passwordHash, $user['id']]);
|
|
|
|
// Envoi de l'email avec le nouveau mot de passe
|
|
$emailSent = ApiService::sendEmail(
|
|
$email,
|
|
$name,
|
|
'lostpwd',
|
|
['username' => $username, 'password' => $newPassword]
|
|
);
|
|
|
|
if ($emailSent) {
|
|
LogService::log('Réinitialisation mot de passe GeoSector réussie', [
|
|
'level' => 'info',
|
|
'userId' => $user['id'],
|
|
'email' => $email
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'success',
|
|
'message' => 'Un nouveau mot de passe a été envoyé à votre adresse email'
|
|
]);
|
|
} else {
|
|
LogService::log('Échec envoi email réinitialisation mot de passe GeoSector', [
|
|
'level' => 'error',
|
|
'userId' => $user['id'],
|
|
'email' => $email
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Impossible d\'envoyer l\'email. Veuillez contacter l\'administrateur.'
|
|
], 500);
|
|
}
|
|
} catch (Exception $e) {
|
|
LogService::log('Erreur lors de la réinitialisation du mot de passe GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Une erreur est survenue. Veuillez réessayer.'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
public function register(): void {
|
|
try {
|
|
$data = Request::getJson();
|
|
|
|
// 1. Validation des données de base
|
|
if (
|
|
!isset($data['email'], $data['name'], $data['amicale_name'], $data['postal_code'], $data['city_name']) ||
|
|
empty($data['email']) || empty($data['name']) || empty($data['amicale_name']) || empty($data['postal_code'])
|
|
) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Tous les champs sont requis'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
// 2. Validation du token et du captcha
|
|
if (!isset($data['token']) || empty($data['token'])) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Token de sécurité manquant'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
// Vérification que le token est un timestamp valide et récent
|
|
// Le frontend envoie un timestamp en millisecondes, donc on le convertit en secondes
|
|
$tokenTimestamp = intval($data['token']) / 1000; // Conversion millisecondes -> secondes
|
|
$currentTime = time();
|
|
$twoHoursAgo = $currentTime - 7200; // 2 heures = 7200 secondes (plus permissif)
|
|
|
|
// Tolérance de 5 minutes pour les décalages d'horloge
|
|
$futureTime = $currentTime + 300; // 5 minutes = 300 secondes
|
|
|
|
// Log pour le débogage
|
|
LogService::log('Vérification du token', [
|
|
'level' => 'info',
|
|
'token_ms' => $data['token'],
|
|
'token_sec' => $tokenTimestamp,
|
|
'current_time' => $currentTime,
|
|
'two_hours_ago' => $twoHoursAgo,
|
|
'future_time' => $futureTime
|
|
]);
|
|
|
|
// Vérification plus permissive
|
|
if ($tokenTimestamp < $twoHoursAgo || $tokenTimestamp > $futureTime) {
|
|
LogService::log('Tentative d\'inscription avec un token invalide', [
|
|
'level' => 'warning',
|
|
'token' => $data['token'],
|
|
'token_sec' => $tokenTimestamp,
|
|
'current_time' => $currentTime,
|
|
'email' => $data['email'] ?? 'non fourni'
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Session expirée, veuillez rafraîchir la page et réessayer'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!isset($data['captcha_answer'], $data['captcha_expected']) ||
|
|
$data['captcha_answer'] != $data['captcha_expected']
|
|
) {
|
|
LogService::log('Tentative d\'inscription avec un captcha invalide', [
|
|
'level' => 'warning',
|
|
'captcha_answer' => $data['captcha_answer'] ?? 'non fourni',
|
|
'captcha_expected' => $data['captcha_expected'] ?? 'non fourni',
|
|
'email' => $data['email'] ?? 'non fourni'
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Vérification anti-robot échouée'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
$email = trim($data['email']);
|
|
$name = trim($data['name']);
|
|
$amicaleName = trim($data['amicale_name']);
|
|
$postalCode = trim($data['postal_code']);
|
|
$cityName = trim($data['city_name'] ?? '');
|
|
|
|
// 3. Validation de l'email
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Format d\'email invalide'
|
|
], 400);
|
|
return;
|
|
}
|
|
|
|
// 4. Vérification de l'existence de l'email
|
|
$encryptedEmail = ApiService::encryptSearchableData($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;
|
|
}
|
|
|
|
// 5. Vérification de l'existence du code postal dans la table entites
|
|
$checkPostalStmt = $this->db->prepare('SELECT id FROM entites WHERE code_postal = ?');
|
|
$checkPostalStmt->execute([$postalCode]);
|
|
if ($checkPostalStmt->fetch()) {
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Une amicale est déjà inscrite à ce code postal'
|
|
], 409);
|
|
return;
|
|
}
|
|
|
|
// 6. Recherche de la région correspondant au code postal
|
|
$departement = substr($postalCode, 0, 2);
|
|
$regionStmt = $this->db->prepare('
|
|
SELECT id FROM x_regions
|
|
WHERE FIND_IN_SET(?, departements) > 0 AND chk_active = 1
|
|
LIMIT 1
|
|
');
|
|
$regionStmt->execute([$departement]);
|
|
$region = $regionStmt->fetch(PDO::FETCH_ASSOC);
|
|
$regionId = $region ? $region['id'] : null;
|
|
|
|
// 7. Chiffrement des données sensibles
|
|
$encryptedName = ApiService::encryptData($name);
|
|
$encryptedAmicaleName = ApiService::encryptData($amicaleName);
|
|
$encryptedEmail = ApiService::encryptSearchableData($email);
|
|
|
|
// 8. Création de l'entité (amicale)
|
|
$this->db->beginTransaction();
|
|
try {
|
|
// Insertion de la nouvelle entité
|
|
$entiteStmt = $this->db->prepare('
|
|
INSERT INTO entites (
|
|
encrypted_name,
|
|
code_postal,
|
|
ville,
|
|
fk_type,
|
|
fk_region,
|
|
encrypted_email,
|
|
chk_demo,
|
|
chk_active,
|
|
created_at
|
|
) VALUES (?, ?, ?, 1, ?, ?, 1, 1, NOW())
|
|
');
|
|
$entiteStmt->execute([
|
|
$encryptedAmicaleName,
|
|
$postalCode,
|
|
$cityName,
|
|
$regionId,
|
|
$encryptedEmail
|
|
]);
|
|
$entiteId = $this->db->lastInsertId();
|
|
|
|
if (!$entiteId) {
|
|
throw new Exception('Erreur lors de la création de l\'entité');
|
|
}
|
|
|
|
// Recherche des coordonnées GPS de la caserne de pompiers
|
|
try {
|
|
$gpsCoordinates = $this->findFireStationCoordinates($postalCode, $cityName);
|
|
|
|
if ($gpsCoordinates) {
|
|
// Mise à jour des coordonnées GPS de l'entité
|
|
$updateGpsStmt = $this->db->prepare('
|
|
UPDATE entites
|
|
SET gps_lat = ?, gps_lng = ?
|
|
WHERE id = ?
|
|
');
|
|
$updateGpsStmt->execute([
|
|
$gpsCoordinates['lat'],
|
|
$gpsCoordinates['lng'],
|
|
$entiteId
|
|
]);
|
|
|
|
LogService::log('Coordonnées GPS de la caserne de pompiers ajoutées', [
|
|
'level' => 'info',
|
|
'entiteId' => $entiteId,
|
|
'postalCode' => $postalCode,
|
|
'cityName' => $cityName,
|
|
'lat' => $gpsCoordinates['lat'],
|
|
'lng' => $gpsCoordinates['lng']
|
|
]);
|
|
} else {
|
|
LogService::log('Aucune caserne de pompiers trouvée', [
|
|
'level' => 'warning',
|
|
'entiteId' => $entiteId,
|
|
'postalCode' => $postalCode,
|
|
'cityName' => $cityName
|
|
]);
|
|
}
|
|
} catch (Exception $e) {
|
|
// On ne bloque pas l'inscription si la recherche de coordonnées échoue
|
|
LogService::log('Erreur lors de la recherche des coordonnées GPS', [
|
|
'level' => 'error',
|
|
'entiteId' => $entiteId,
|
|
'postalCode' => $postalCode,
|
|
'cityName' => $cityName,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
|
|
// 9. Génération du nom d'utilisateur et du mot de passe
|
|
$username = ApiService::generateUserName($this->db, $name, $postalCode, $cityName);
|
|
$encryptedUsername = ApiService::encryptSearchableData($username);
|
|
$password = ApiService::generateSecurePassword();
|
|
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
|
|
|
|
// 10. Création de l'utilisateur administrateur
|
|
$userStmt = $this->db->prepare('
|
|
INSERT INTO users (
|
|
encrypted_user_name,
|
|
encrypted_email,
|
|
user_pass_hash,
|
|
encrypted_name,
|
|
fk_role,
|
|
created_at,
|
|
chk_active,
|
|
fk_entite
|
|
) VALUES (?, ?, ?, ?, 2, NOW(), 1, ?)
|
|
');
|
|
$userStmt->execute([
|
|
$encryptedUsername,
|
|
$encryptedEmail,
|
|
$passwordHash,
|
|
$encryptedName,
|
|
$entiteId
|
|
]);
|
|
$userId = $this->db->lastInsertId();
|
|
|
|
$this->db->commit();
|
|
|
|
// Log du succès de l'inscription
|
|
LogService::log('Inscription GeoSector réussie', [
|
|
'level' => 'info',
|
|
'userId' => $userId,
|
|
'username' => $username,
|
|
'email' => $email,
|
|
'role' => 2,
|
|
'entiteId' => $entiteId,
|
|
'amicaleName' => $amicaleName,
|
|
'postalCode' => $postalCode,
|
|
'cityName' => $cityName
|
|
]);
|
|
|
|
// 11. Envoi des emails
|
|
// Premier email : bienvenue avec UNIQUEMENT le nom d'utilisateur (sans mot de passe)
|
|
// Création d'un mot de passe temporaire pour le template (ne sera pas affiché)
|
|
$tempPassword = "********";
|
|
$welcomeResult = ApiService::sendEmail(
|
|
$email,
|
|
$name,
|
|
'welcome',
|
|
['username' => $username, 'password' => $tempPassword]
|
|
);
|
|
|
|
// Email de notification aux administrateurs (sans le nom d'utilisateur ni le mot de passe)
|
|
$notificationMessage = "Nouvelle inscription GeoSector:\n\n" .
|
|
"Nom: $name\n" .
|
|
"Email: $email\n" .
|
|
"Amicale: $amicaleName\n" .
|
|
"Code postal: $postalCode\n" .
|
|
"Ville: $cityName\n";
|
|
|
|
ApiService::sendEmail(
|
|
"contactgeosector@gmail.com",
|
|
"Admin GeoSector",
|
|
'alert',
|
|
['subject' => 'Nouvelle inscription GeoSector', 'message' => $notificationMessage]
|
|
);
|
|
|
|
ApiService::sendEmail(
|
|
"contact@geosector.fr",
|
|
"Admin GeoSector",
|
|
'alert',
|
|
['subject' => 'Nouvelle inscription GeoSector', 'message' => $notificationMessage]
|
|
);
|
|
|
|
// Attendre un court délai avant d'envoyer le second email (pour éviter les filtres anti-spam)
|
|
sleep(2);
|
|
|
|
// Second email : UNIQUEMENT le mot de passe
|
|
$passwordResult = ApiService::sendEmail(
|
|
$email,
|
|
$name,
|
|
'lostpwd',
|
|
['username' => $username, 'password' => $password]
|
|
);
|
|
|
|
// Réponse selon le résultat de l'envoi d'email
|
|
if ($welcomeResult === 0 || $passwordResult === 0) {
|
|
Response::json([
|
|
'status' => 'warning',
|
|
'message' => 'Compte créé avec succès mais impossible de vous envoyer tous les emails. ' .
|
|
'Rendez-vous sur la page de login et choisissez mot de passe perdu pour recevoir votre mot de passe.'
|
|
], 201);
|
|
} else {
|
|
Response::json([
|
|
'status' => 'success',
|
|
'message' => 'Votre compte a bien été créé et vous recevrez par email votre identifiant et mot de passe'
|
|
], 201);
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->db->rollBack();
|
|
LogService::log('Erreur lors de la création du compte GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage(),
|
|
'email' => $email,
|
|
'amicaleName' => $amicaleName,
|
|
'postalCode' => $postalCode
|
|
]);
|
|
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => $e->getMessage()
|
|
], 500);
|
|
return;
|
|
}
|
|
} catch (PDOException $e) {
|
|
LogService::log('Erreur serveur lors de l\'inscription GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage(),
|
|
'code' => $e->getCode(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Erreur lors de la création du compte. Veuillez réessayer.'
|
|
], 500);
|
|
} catch (Exception $e) {
|
|
LogService::log('Erreur inattendue lors de l\'inscription GeoSector', [
|
|
'level' => 'error',
|
|
'error' => $e->getMessage()
|
|
]);
|
|
Response::json([
|
|
'status' => 'error',
|
|
'message' => 'Une erreur inattendue est survenue. Veuillez réessayer.'
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
// Méthodes auxiliaires
|
|
|
|
public function logout(): void {
|
|
$userId = Session::getUserId() ?? null;
|
|
$userEmail = Session::getUserEmail() ?? 'anonyme';
|
|
|
|
Session::logout();
|
|
|
|
LogService::log('Déconnexion GeoSector réussie', [
|
|
'level' => 'info',
|
|
'userId' => $userId,
|
|
'email' => $userEmail
|
|
]);
|
|
|
|
// Retourner une réponse standardisée
|
|
Response::json([
|
|
'status' => 'success',
|
|
'message' => 'Déconnexion réussie'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Recherche les coordonnées GPS d'une caserne de pompiers dans une ville donnée
|
|
*
|
|
* @param string $postalCode Le code postal de la ville
|
|
* @param string $cityName Le nom de la ville
|
|
* @return array|null Tableau associatif contenant les coordonnées GPS (lat, lng) ou null si aucune caserne trouvée
|
|
* @throws Exception En cas d'erreur lors de la requête API
|
|
*/
|
|
private function findFireStationCoordinates(string $postalCode, string $cityName): ?array {
|
|
// Mots-clés pour rechercher une caserne de pompiers
|
|
$keywords = ['pompiers', 'sdis', 'sapeurs-pompiers', 'caserne', 'centre de secours'];
|
|
|
|
// Formater la ville et le code postal pour la recherche
|
|
$citySearch = urlencode($cityName . ' ' . $postalCode);
|
|
|
|
foreach ($keywords as $keyword) {
|
|
// Construire l'URL de recherche pour l'API adresse.gouv.fr
|
|
$searchUrl = "https://api-adresse.data.gouv.fr/search/?q=" . urlencode($keyword) . "+$citySearch&limit=5";
|
|
|
|
// Effectuer la requête HTTP
|
|
$response = @file_get_contents($searchUrl);
|
|
|
|
if ($response === false) {
|
|
LogService::log('Erreur lors de la requête à l\'API adresse.gouv.fr', [
|
|
'level' => 'error',
|
|
'url' => $searchUrl
|
|
]);
|
|
continue; // Essayer avec le mot-clé suivant
|
|
}
|
|
|
|
// Décoder la réponse JSON
|
|
$data = json_decode($response, true);
|
|
|
|
if (!$data || !isset($data['features']) || empty($data['features'])) {
|
|
continue; // Aucun résultat, essayer avec le mot-clé suivant
|
|
}
|
|
|
|
// Parcourir les résultats pour trouver une caserne de pompiers
|
|
foreach ($data['features'] as $feature) {
|
|
$properties = $feature['properties'] ?? [];
|
|
$name = strtolower($properties['name'] ?? '');
|
|
$label = strtolower($properties['label'] ?? '');
|
|
|
|
// Vérifier si le résultat correspond à une caserne de pompiers
|
|
if (
|
|
strpos($name, 'pompier') !== false ||
|
|
strpos($name, 'sdis') !== false ||
|
|
strpos($label, 'pompier') !== false ||
|
|
strpos($label, 'sdis') !== false ||
|
|
strpos($name, 'caserne') !== false ||
|
|
strpos($label, 'caserne') !== false ||
|
|
strpos($name, 'centre de secours') !== false ||
|
|
strpos($label, 'centre de secours') !== false
|
|
) {
|
|
// Extraire les coordonnées GPS
|
|
$coordinates = $feature['geometry']['coordinates'] ?? null;
|
|
|
|
if ($coordinates && count($coordinates) >= 2) {
|
|
// L'API retourne les coordonnées au format [longitude, latitude]
|
|
return [
|
|
'lng' => $coordinates[0],
|
|
'lat' => $coordinates[1]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si aucune caserne n'a été trouvée avec les mots-clés, utiliser les coordonnées du centre de la ville
|
|
$cityUrl = "https://api-adresse.data.gouv.fr/search/?q=$citySearch&limit=1";
|
|
$cityResponse = @file_get_contents($cityUrl);
|
|
|
|
if ($cityResponse !== false) {
|
|
$cityData = json_decode($cityResponse, true);
|
|
|
|
if ($cityData && isset($cityData['features'][0]['geometry']['coordinates'])) {
|
|
$coordinates = $cityData['features'][0]['geometry']['coordinates'];
|
|
|
|
LogService::log('Utilisation des coordonnées du centre de la ville', [
|
|
'level' => 'info',
|
|
'city' => $cityName,
|
|
'postalCode' => $postalCode
|
|
]);
|
|
|
|
return [
|
|
'lng' => $coordinates[0],
|
|
'lat' => $coordinates[1]
|
|
];
|
|
}
|
|
}
|
|
|
|
// Aucune coordonnée trouvée
|
|
return null;
|
|
}
|
|
}
|