Files
geo/api/src/Core/Router.php

285 lines
12 KiB
PHP
Executable File

<?php
declare(strict_types=1);
class Router {
// Préfixe fixe de l'API (toujours 'api')
private const API_PREFIX = 'api';
private array $routes = [];
private array $publicEndpoints = [
'login',
'register',
'lostpassword',
'log',
'villes', // Ajout de la route villes comme endpoint public pour l'autocomplétion du code postal
'password/check', // Vérification de la force des mots de passe (public pour l'inscription)
'password/compromised', // Vérification si un mot de passe est compromis
];
public function __construct() {
// Pas besoin de récupérer AppConfig puisque nous utilisons une constante pour le préfixe API
$this->configureRoutes();
}
/**
* Configure toutes les routes de l'application
*/
private function configureRoutes(): void {
// Routes publiques
$this->post('login', ['LoginController', 'login']);
$this->post('register', ['LoginController', 'register']);
$this->post('lostpassword', ['LoginController', 'lostPassword']);
// Route pour les logs
$this->post('log', ['LogController', 'index']);
// Routes privées utilisateurs
// IMPORTANT: Les routes spécifiques doivent être déclarées AVANT les routes avec paramètres
$this->post('users/check-username', ['UserController', 'checkUsername']); // Déplacé avant les routes avec :id
$this->get('users', ['UserController', 'getUsers']);
$this->get('users/:id', ['UserController', 'getUserById']);
$this->post('users', ['UserController', 'createUser']);
$this->put('users/:id', ['UserController', 'updateUser']);
$this->delete('users/:id', ['UserController', 'deleteUser']);
$this->post('users/:id/reset-password', ['UserController', 'resetPassword']);
$this->post('logout', ['LoginController', 'logout']);
// Routes entités
$this->get('entites', ['EntiteController', 'getEntites']);
$this->get('entites/:id', ['EntiteController', 'getEntiteById']);
$this->get('entites/postal/:code', ['EntiteController', 'getEntiteByPostalCode']);
$this->put('entites/:id', ['EntiteController', 'updateEntite']);
$this->post('entites/:id/logo', ['EntiteController', 'uploadLogo']);
$this->get('entites/:id/logo', ['EntiteController', 'getLogo']);
// Routes opérations
$this->get('operations', ['OperationController', 'getOperations']);
$this->get('operations/:id', ['OperationController', 'getOperationById']);
$this->post('operations', ['OperationController', 'createOperation']);
$this->put('operations/:id', ['OperationController', 'updateOperation']);
$this->delete('operations/:id', ['OperationController', 'deleteOperation']);
// Routes d'export d'opérations
$this->get('operations/:id/export/excel', ['OperationController', 'exportExcel']);
$this->get('operations/:id/export/json', ['OperationController', 'exportJson']);
$this->get('operations/:id/export/full', ['OperationController', 'exportFull']);
$this->get('operations/:id/backups', ['OperationController', 'getBackups']);
$this->get('operations/:id/backups/:backup_id', ['OperationController', 'downloadBackup']);
$this->delete('operations/:id/backups/:backup_id', ['OperationController', 'deleteBackup']);
// Routes passages
$this->get('passages', ['PassageController', 'getPassages']);
$this->get('passages/:id', ['PassageController', 'getPassageById']);
$this->get('passages/:id/receipt', ['PassageController', 'getReceipt']);
$this->get('passages/operation/:operation_id', ['PassageController', 'getPassagesByOperation']);
$this->post('passages', ['PassageController', 'createPassage']);
$this->put('passages/:id', ['PassageController', 'updatePassage']);
$this->delete('passages/:id', ['PassageController', 'deletePassage']);
// Routes villes
$this->get('villes', ['VilleController', 'searchVillesByPostalCode']);
// Routes fichiers
$this->get('files/browse', ['FileController', 'browse']);
$this->get('files/search', ['FileController', 'search']);
$this->get('files/stats', ['FileController', 'getStats']);
$this->get('files/metadata', ['FileController', 'getMetadata']);
$this->get('files/list/:support/:id', ['FileController', 'listBySupport']);
$this->get('files/info/:id', ['FileController', 'getFileInfo']);
$this->get('files/download/:id', ['FileController', 'download']);
$this->delete('files/:id', ['FileController', 'deleteFile']);
// Routes secteurs
$this->get('sectors', ['SectorController', 'index']);
$this->post('sectors', ['SectorController', 'create']);
$this->put('sectors/:id', ['SectorController', 'update']);
$this->delete('sectors/:id', ['SectorController', 'delete']);
// Routes mots de passe
$this->post('password/check', ['PasswordController', 'checkStrength']);
$this->post('password/compromised', ['PasswordController', 'checkCompromised']);
$this->get('password/generate', ['PasswordController', 'generate']);
// Routes du module Chat
$this->get('chat/rooms', ['ChatController', 'getRooms']);
$this->post('chat/rooms', ['ChatController', 'createRoom']);
$this->put('chat/rooms/:id', ['ChatController', 'updateRoom']);
$this->delete('chat/rooms/:id', ['ChatController', 'deleteRoom']);
$this->get('chat/rooms/:id/messages', ['ChatController', 'getRoomMessages']);
$this->post('chat/rooms/:id/messages', ['ChatController', 'sendMessage']);
$this->put('chat/messages/:id', ['ChatController', 'updateMessage']);
$this->post('chat/rooms/:id/read', ['ChatController', 'markAsRead']);
$this->get('chat/recipients', ['ChatController', 'getRecipients']);
// Routes du module Sécurité (Admin uniquement)
$this->get('admin/metrics', ['SecurityController', 'getMetrics']);
$this->get('admin/alerts', ['SecurityController', 'getAlerts']);
$this->post('admin/alerts/:id/resolve', ['SecurityController', 'resolveAlert']);
$this->get('admin/blocked-ips', ['SecurityController', 'getBlockedIPs']);
$this->post('admin/unblock-ip', ['SecurityController', 'unblockIP']);
$this->post('admin/block-ip', ['SecurityController', 'blockIP']);
$this->get('admin/security-report', ['SecurityController', 'getSecurityReport']);
$this->post('admin/cleanup', ['SecurityController', 'cleanup']);
$this->post('admin/test-alert', ['SecurityController', 'testAlert']);
}
public function handle(): void {
$method = $_SERVER['REQUEST_METHOD'];
$uri = $this->normalizeUri($_SERVER['REQUEST_URI']);
error_log("Initial URI: $uri");
// Handle CORS preflight
if ($method === 'OPTIONS') {
header('HTTP/1.1 200 OK');
exit();
}
// Prendre le préfixe API à partir de la constante
$apiPrefix = self::API_PREFIX;
// Vérifier si l'URI commence bien par le préfixe API
$prefixMatch = strpos($uri, $apiPrefix) === 0;
if (!$prefixMatch) {
Response::json([
'error' => 'Invalid API prefix',
'path' => $uri,
'expected_prefix' => $apiPrefix
], 404);
return;
}
// Extraire l'endpoint en retirant le préfixe API
$endpoint = substr($uri, strlen($apiPrefix) + 1); // +1 pour le slash
$endpoint = trim($endpoint, '/');
// Check if endpoint is public
if ($this->isPublicEndpoint($endpoint)) {
error_log("Public endpoint found: $endpoint");
$route = $this->findRoute($method, $endpoint);
if ($route) {
$this->executeRoute($route);
return;
}
} else {
error_log("Private endpoint: $endpoint");
// Private route - check auth first
Session::requireAuth();
$route = $this->findRoute($method, $endpoint);
if ($route) {
$this->executeRoute($route);
return;
}
}
// No route found
Response::json([
'error' => 'Route not found',
'endpoint' => $endpoint,
'uri' => $uri
], 404);
}
private function normalizeUri(string $uri): string {
return trim(preg_replace('#/+#', '/', parse_url($uri, PHP_URL_PATH)), '/');
}
private function isPublicEndpoint(string $endpoint): bool {
return in_array($endpoint, $this->publicEndpoints);
}
private function executeRoute(array $route): void {
[$controllerName, $method] = $route['handler'];
// Essayer de trouver le contrôleur en tenant compte des namespaces possibles
$classNames = [
$controllerName, // Sans namespace
"\\App\\Controllers\\$controllerName", // Avec namespace complet
"\\$controllerName" // Avec namespace racine
];
$controllerClass = null;
foreach ($classNames as $className) {
if (class_exists($className)) {
$controllerClass = $className;
break;
}
}
if ($controllerClass === null) {
// Classe non trouvée, gérer l'erreur
Response::json([
'error' => 'Controller not found',
'controller' => $controllerName,
'status' => 'error',
'message' => 'Controller not found',
'tried_namespaces' => implode(', ', $classNames)
], 404);
return;
}
$controller = new $controllerClass();
if (!empty($route['params'])) {
$controller->$method(...$route['params']);
} else {
$controller->$method();
}
}
public function get(string $path, array $handler): void {
$this->addRoute('GET', $path, $handler);
}
public function post(string $path, array $handler): void {
$this->addRoute('POST', $path, $handler);
}
public function put(string $path, array $handler): void {
$this->addRoute('PUT', $path, $handler);
}
public function delete(string $path, array $handler): void {
$this->addRoute('DELETE', $path, $handler);
}
private function addRoute(string $method, string $path, array $handler): void {
// Normalize the path
$path = trim($path, '/');
$this->routes[$method][$path] = $handler;
}
private function findRoute(string $method, string $uri): ?array {
if (!isset($this->routes[$method])) {
error_log("Méthode $method non trouvée dans les routes");
return null;
}
$uri = trim($uri, '/');
error_log("Recherche de route pour: méthode=$method, uri=$uri");
error_log("Routes disponibles pour $method: " . implode(', ', array_keys($this->routes[$method])));
foreach ($this->routes[$method] as $route => $handler) {
// Correction: utiliser :param au lieu de {param}
$pattern = preg_replace('/:([^\/]+)/', '([^/]+)', $route);
$pattern = "@^" . $pattern . "$@D";
error_log("Test pattern: $pattern contre uri: $uri");
if (preg_match($pattern, $uri, $matches)) {
error_log("Route trouvée! Pattern: $pattern, Handler: {$handler[0]}::{$handler[1]}");
array_shift($matches);
return [
'handler' => $handler,
'params' => $matches
];
}
}
error_log("Aucune route trouvée pour $method $uri");
return null;
}
}