285 lines
12 KiB
PHP
Executable File
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;
|
|
}
|
|
}
|