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']); // Routes Stripe // Configuration et onboarding $this->post('stripe/accounts', ['StripeController', 'createAccount']); $this->get('stripe/accounts/:entityId/status', ['StripeController', 'getAccountStatus']); $this->post('stripe/accounts/:accountId/onboarding-link', ['StripeController', 'createOnboardingLink']); $this->post('stripe/locations', ['StripeController', 'createLocation']); // Terminal et Tap to Pay $this->post('stripe/terminal/connection-token', ['StripeController', 'createConnectionToken']); $this->post('stripe/devices/check-tap-to-pay', ['StripeController', 'checkTapToPayCapability']); $this->get('stripe/devices/certified-android', ['StripeController', 'getCertifiedAndroidDevices']); // Paiements $this->post('stripe/payments/create-intent', ['StripeController', 'createPaymentIntent']); $this->get('stripe/payments/:paymentIntentId', ['StripeController', 'getPaymentStatus']); // Statistiques et configuration $this->get('stripe/stats', ['StripeController', 'getPaymentStats']); $this->get('stripe/config', ['StripeController', 'getPublicConfig']); // Webhook (IMPORTANT: pas d'authentification requise pour les webhooks Stripe) $this->post('stripe/webhook', ['StripeWebhookController', 'handleWebhook']); } 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, '/'); // Désactiver les logs de debug en production // 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; } }