feat: Gestion des secteurs et migration v3.0.4+304
- Ajout système complet de gestion des secteurs avec contours géographiques - Import des contours départementaux depuis GeoJSON - API REST pour la gestion des secteurs (/api/sectors) - Service de géolocalisation pour déterminer les secteurs - Migration base de données avec tables x_departements_contours et sectors_adresses - Interface Flutter pour visualisation et gestion des secteurs - Ajout thème sombre dans l'application - Corrections diverses et optimisations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
0
api/src/Controllers/EntiteController.php
Normal file → Executable file
0
api/src/Controllers/EntiteController.php
Normal file → Executable file
0
api/src/Controllers/FileController.php
Normal file → Executable file
0
api/src/Controllers/FileController.php
Normal file → Executable file
0
api/src/Controllers/LogController.php
Normal file → Executable file
0
api/src/Controllers/LogController.php
Normal file → Executable file
30
api/src/Controllers/LoginController.php
Normal file → Executable file
30
api/src/Controllers/LoginController.php
Normal file → Executable file
@@ -49,8 +49,8 @@ class LoginController {
|
||||
|
||||
// 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';
|
||||
// user accessible uniquement aux fk_role=1
|
||||
$roleCondition = ($interface === 'user') ? 'AND fk_role=1' : 'AND fk_role>1';
|
||||
|
||||
// Log pour le debug
|
||||
LogService::log('Tentative de connexion GeoSector', [
|
||||
@@ -159,6 +159,32 @@ class LoginController {
|
||||
'fk_entite' => $user['fk_entite'] ?? '0',
|
||||
];
|
||||
Session::login($sessionData);
|
||||
|
||||
// Vérifier et exécuter l'initialisation des contours départementaux pour d6soft
|
||||
if ($username === 'd6soft') {
|
||||
require_once __DIR__ . '/../../scripts/init_departements_contours.php';
|
||||
$initLog = \DepartementContoursInitializer::runIfNeeded($this->db, $username);
|
||||
|
||||
if ($initLog !== null) {
|
||||
// Logger l'initialisation
|
||||
LogService::log('Initialisation des contours départementaux', [
|
||||
'level' => 'info',
|
||||
'username' => $username,
|
||||
'log_count' => count($initLog)
|
||||
]);
|
||||
|
||||
// Logger aussi les dernières lignes du log pour diagnostic
|
||||
$lastLines = array_slice($initLog, -5);
|
||||
foreach ($lastLines as $line) {
|
||||
if (strpos($line, '✗') !== false || strpos($line, 'terminé') !== false) {
|
||||
LogService::log('Import contours: ' . $line, [
|
||||
'level' => 'info',
|
||||
'username' => $username
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Préparation des données utilisateur pour la réponse (uniquement les champs du user)
|
||||
$userData = [
|
||||
|
||||
0
api/src/Controllers/OperationController.php
Normal file → Executable file
0
api/src/Controllers/OperationController.php
Normal file → Executable file
0
api/src/Controllers/PassageController.php
Normal file → Executable file
0
api/src/Controllers/PassageController.php
Normal file → Executable file
1307
api/src/Controllers/SectorController.php
Normal file
1307
api/src/Controllers/SectorController.php
Normal file
File diff suppressed because it is too large
Load Diff
162
api/src/Controllers/UserController.php
Normal file → Executable file
162
api/src/Controllers/UserController.php
Normal file → Executable file
@@ -550,28 +550,27 @@ class UserController {
|
||||
|
||||
// ——— Gestion du transfert éventuel ———
|
||||
$transferTo = isset($_GET['transfer_to']) ? trim($_GET['transfer_to']) : null;
|
||||
$operationId = isset($_GET['operation_id']) ? trim($_GET['operation_id']) : null;
|
||||
|
||||
if (($transferTo && !$operationId) || (!$transferTo && $operationId)) {
|
||||
Response::json([
|
||||
'status' => 'error',
|
||||
'message' => "Il faut fournir transfer_to ET operation_id ou aucun des deux"
|
||||
], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($transferTo && $operationId) {
|
||||
if ($transferTo) {
|
||||
try {
|
||||
// Transférer TOUS les passages de l'utilisateur vers l'utilisateur désigné
|
||||
$stmt3 = $this->db->prepare('
|
||||
UPDATE passages
|
||||
UPDATE ope_pass
|
||||
SET fk_user = :new_user_id
|
||||
WHERE fk_user = :delete_user_id
|
||||
AND fk_operation = :operation_id
|
||||
');
|
||||
$stmt3->execute([
|
||||
'new_user_id' => $transferTo,
|
||||
'delete_user_id' => $id,
|
||||
'operation_id' => $operationId
|
||||
'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([
|
||||
@@ -589,7 +588,9 @@ class UserController {
|
||||
$stmtOpeUsers = $this->db->prepare('DELETE FROM ope_users WHERE fk_user = ?');
|
||||
$stmtOpeUsers->execute([$id]);
|
||||
|
||||
// Ici éventuellement : d'autres suppressions en cascade si besoin
|
||||
// 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]);
|
||||
@@ -606,7 +607,7 @@ class UserController {
|
||||
'level' => 'info',
|
||||
'deletedBy' => $currentUserId,
|
||||
'userId' => $id,
|
||||
'passage_transfer' => $transferTo && $operationId ? "Vers utilisateur $transferTo pour operation $operationId" : 'Aucun'
|
||||
'passage_transfer' => $transferTo ? "Tous les passages transférés vers utilisateur $transferTo" : 'Aucun transfert'
|
||||
]);
|
||||
|
||||
Response::json([
|
||||
@@ -626,6 +627,135 @@ class UserController {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes auxiliaires
|
||||
private function validateUpdateData(array $data): ?string {
|
||||
// Validation de l'email
|
||||
|
||||
0
api/src/Controllers/VilleController.php
Normal file → Executable file
0
api/src/Controllers/VilleController.php
Normal file → Executable file
Reference in New Issue
Block a user