$val) { if (strcasecmp($key, 'Authorization') === 0) { $authHeader = trim($val); break; } if (strcasecmp($key, 'X-API-KEY') === 0) { return trim($val); } } } if ($authHeader !== null && preg_match('/Bearer\s+(\S+)/i', $authHeader, $matches)) { return $matches[1]; } if (!empty($_GET['token'])) { return trim((string)$_GET['token']); } return null; } /** * Vérifie l'authentification et les droits de l'utilisateur */ function authenticate(PDO $db, bool $required = true, ?string $requiredRole = null): ?array { $token = getBearerToken(); if ($token === null) { if ($required) { sendJson(401, [ 'success' => false, 'error' => "Authentification requise. Veuillez fournir un jeton 'Authorization: Bearer ' ou 'X-API-KEY'." ]); } return null; } $stmt = $db->prepare(' SELECT id, username, nom, role, api_token, token_expires_at FROM users WHERE api_token = ? LIMIT 1 '); $stmt->execute([$token]); $user = $stmt->fetch(); if (!$user) { sendJson(401, [ 'success' => false, 'error' => "Jeton d'authentification invalide. Veuillez vous reconnecter via POST /auth/login." ]); } // Vérifier l'expiration du jeton if (!empty($user['token_expires_at']) && strtotime($user['token_expires_at']) < time()) { sendJson(401, [ 'success' => false, 'error' => "Jeton d'authentification expiré. Veuillez vous reconnecter via POST /auth/login." ]); } // Vérifier le rôle requis (ex: 'admin') if ($requiredRole !== null && strtolower((string)$user['role']) !== strtolower($requiredRole)) { sendJson(403, [ 'success' => false, 'error' => "Accès refusé. Privilèges insuffisants (rôle '$requiredRole' requis, rôle actuel : '{$user['role']}')." ]); } return $user; } /** * Recherche d'une voiture par sa plaque d'immatriculation */ function findCarByPlate(PDO $db, string $plaque): void { $clean = strtoupper(trim(urldecode($plaque))); $norm = str_replace(['-', ' ', '.', '_'], '', $clean); $stmt = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE UPPER(c.immatriculation) = :raw OR REPLACE(REPLACE(REPLACE(REPLACE(UPPER(c.immatriculation), "-", ""), " ", ""), ".", ""), "_", "") = :norm LIMIT 1 '); $stmt->execute([':raw' => $clean, ':norm' => $norm]); $car = $stmt->fetch(); if (!$car) { sendJson(404, [ 'success' => false, 'error' => "Aucune voiture trouvée avec l'immatriculation '$clean'" ]); } sendJson(200, [ 'success' => true, 'data' => $car ]); } try { $db = getDB(); $method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET'); $segments = getPathSegments(); // Route racine: Documentation des points d'accès if (empty($segments)) { sendJson(200, [ 'success' => true, 'message' => 'API Voitures avec Authentification opérationnelle', 'database' => DB_NAME, 'comptes_demo' => [ 'admin' => ['username' => 'admin', 'password' => 'adminpassword', 'token_fixe' => 'admin-token-secret-12345'], 'user' => ['username' => 'user', 'password' => 'userpassword', 'token_fixe' => 'user-token-secret-67890'] ], 'endpoints' => [ 'Authentification' => [ 'POST /auth/login' => '[PUBLIC] Se connecter et obtenir un jeton (username, password)', 'POST /auth/register' => '[PUBLIC] Créer un compte utilisateur (username, password, nom)', 'GET /auth/me' => '[AUTHENTIFIÉ] Obtenir le profil de l\'utilisateur connecté', 'POST /auth/logout' => '[AUTHENTIFIÉ] Révoquer le jeton de session' ], 'Voitures' => [ 'GET /cars' => '[PUBLIC] Liste des voitures (filtrable par ?immatriculation=...)', 'GET /cars/{id}' => '[PUBLIC] Détails d\'une voiture', 'GET /cars/immatriculation/{plaque}' => '[PUBLIC] Identifier une voiture par sa plaque', 'GET /cars/{id}/etat' => '[AUTHENTIFIÉ] État complet (voiture, dernier km, entretiens, notes)', 'POST /cars' => '[AUTHENTIFIÉ] Créer une voiture (marque, modele, annee, dateAchat, VIN, immatriculation)', 'PUT /cars/{id}' => '[AUTHENTIFIÉ] Modifier les paramètres d\'une voiture', 'DELETE /cars/{id}' => '[ADMIN] Supprimer une voiture et toutes ses données associées' ], 'Kilométrage' => [ 'GET /cars/{id}/kilometrage' => '[PUBLIC] Historique des relevés kilométriques', 'POST /cars/{id}/kilometrage' => '[AUTHENTIFIÉ] Ajouter un relevé kilométrique (valeur, date_releve)', 'DELETE /kilometrage/{id}' => '[ADMIN] Supprimer un relevé kilométrique' ], 'Entretiens' => [ 'GET /cars/{id}/maintenance' => '[PUBLIC] Historique des entretiens d\'une voiture', 'GET /maintenance/{id}' => '[PUBLIC] Détails d\'un entretien', 'POST /cars/{id}/maintenance' => '[AUTHENTIFIÉ] Ajouter un entretien (type_entretien, date_evenement, kilometrage, ...)', 'PUT /maintenance/{id}' => '[AUTHENTIFIÉ] Modifier un entretien', 'DELETE /maintenance/{id}' => '[ADMIN] Supprimer un entretien' ], 'Notes (Contenu interne)' => [ 'GET /cars/{id}/notes' => '[AUTHENTIFIÉ] Consulter les notes d\'une voiture', 'POST /cars/{id}/notes' => '[AUTHENTIFIÉ] Ajouter une note (titre, contenu)', 'PUT /notes/{id}' => '[AUTHENTIFIÉ] Modifier une note', 'DELETE /notes/{id}' => '[AUTHENTIFIÉ] Supprimer une note' ] ] ]); } $resource = strtolower($segments[0]); // ========================================================================= // RESSOURCE: AUTHENTIFICATION (/auth ...) // ========================================================================= if ($resource === 'auth') { $action = strtolower($segments[1] ?? ''); // POST /auth/login if ($action === 'login' && $method === 'POST') { $body = getBody(); if (empty($body['username']) || empty($body['password'])) { sendJson(400, ['success' => false, 'error' => 'Champs username et password obligatoires']); } $stmt = $db->prepare('SELECT * FROM users WHERE username = ? LIMIT 1'); $stmt->execute([trim((string)$body['username'])]); $user = $stmt->fetch(); if (!$user || !password_verify((string)$body['password'], $user['password'])) { sendJson(401, ['success' => false, 'error' => 'Identifiants invalides (nom d\'utilisateur ou mot de passe incorrect)']); } // Génération d'un nouveau jeton valide 7 jours $token = bin2hex(random_bytes(32)); $expiresAt = date('Y-m-d H:i:s', time() + (7 * 86400)); $stmtUpdate = $db->prepare('UPDATE users SET api_token = ?, token_expires_at = ? WHERE id = ?'); $stmtUpdate->execute([$token, $expiresAt, $user['id']]); sendJson(200, [ 'success' => true, 'message' => 'Connexion réussie', 'data' => [ 'user' => [ 'id' => (int)$user['id'], 'username' => $user['username'], 'nom' => $user['nom'], 'role' => $user['role'] ], 'token' => $token, 'expires_at' => $expiresAt ] ]); } // POST /auth/register if ($action === 'register' && $method === 'POST') { $body = getBody(); if (empty($body['username']) || empty($body['password'])) { sendJson(400, ['success' => false, 'error' => 'Champs username et password obligatoires']); } $username = trim((string)$body['username']); $nom = !empty($body['nom']) ? trim((string)$body['nom']) : null; $password = password_hash((string)$body['password'], PASSWORD_DEFAULT); // Vérifier existence $stmtCheck = $db->prepare('SELECT id FROM users WHERE username = ?'); $stmtCheck->execute([$username]); if ($stmtCheck->fetch()) { sendJson(409, ['success' => false, 'error' => "Le nom d'utilisateur '$username' est déjà utilisé"]); } $token = bin2hex(random_bytes(32)); $expiresAt = date('Y-m-d H:i:s', time() + (7 * 86400)); $stmt = $db->prepare('INSERT INTO users (username, password, nom, role, api_token, token_expires_at) VALUES (?, ?, ?, ?, ?, ?)'); $stmt->execute([$username, $password, $nom, 'user', $token, $expiresAt]); $newId = (int)$db->lastInsertId(); sendJson(201, [ 'success' => true, 'message' => 'Compte créé avec succès', 'data' => [ 'user' => [ 'id' => $newId, 'username' => $username, 'nom' => $nom, 'role' => 'user' ], 'token' => $token, 'expires_at' => $expiresAt ] ]); } // GET /auth/me (Profil de l'utilisateur connecté) if ($action === 'me' && $method === 'GET') { $user = authenticate($db, true); sendJson(200, [ 'success' => true, 'data' => [ 'id' => (int)$user['id'], 'username' => $user['username'], 'nom' => $user['nom'], 'role' => $user['role'], 'token_expires_at' => $user['token_expires_at'] ] ]); } // POST /auth/logout (Révocation du jeton) if ($action === 'logout' && $method === 'POST') { $user = authenticate($db, true); $stmt = $db->prepare('UPDATE users SET api_token = NULL, token_expires_at = NULL WHERE id = ?'); $stmt->execute([$user['id']]); sendJson(200, ['success' => true, 'message' => 'Déconnexion réussie, le jeton a été révoqué']); } sendJson(404, ['success' => false, 'error' => "Action d'authentification '$action' non reconnue"]); } // ========================================================================= // ENDPOINT DIRECT: /immatriculation/{plaque} // ========================================================================= if ($resource === 'immatriculation' && $method === 'GET') { $plaque = $segments[1] ?? null; if (empty($plaque)) { sendJson(400, ['success' => false, 'error' => 'Numéro d\'immatriculation manquant dans l\'URL']); } findCarByPlate($db, $plaque); } // ========================================================================= // RESSOURCE: CARS (/cars ...) // ========================================================================= if ($resource === 'cars' || $resource === 'voitures') { // GET /cars/immatriculation/{plaque} (Public) if (isset($segments[1]) && strtolower($segments[1]) === 'immatriculation' && $method === 'GET') { $plaque = $segments[2] ?? null; if (empty($plaque)) { sendJson(400, ['success' => false, 'error' => 'Numéro d\'immatriculation manquant dans l\'URL']); } findCarByPlate($db, $plaque); } $carId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; $subResource = $segments[2] ?? null; // 1. GET /cars/{id}/etat [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] if ($carId && $subResource === 'etat' && $method === 'GET') { $currentUser = authenticate($db, true); try { $stmt = $db->prepare('CALL sp_getEtatVoiture(:id)'); $stmt->execute([':id' => $carId]); $car = $stmt->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $maintenances = []; if ($stmt->nextRowset()) { $maintenances = $stmt->fetchAll(); } $notes = []; if ($stmt->nextRowset()) { $notes = $stmt->fetchAll(); } $stmt->closeCursor(); sendJson(200, [ 'success' => true, 'consulted_by' => $currentUser['username'], 'data' => [ 'voiture' => $car, 'maintenances' => $maintenances, 'notes' => $notes ] ]); } catch (Exception $e) { $stmtCar = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtCar->execute([$carId]); $car = $stmtCar->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $stmtMaint = $db->prepare('SELECT * FROM maintenance_logs WHERE car_id = ? ORDER BY date_evenement DESC'); $stmtMaint->execute([$carId]); $stmtNotes = $db->prepare('SELECT * FROM notes WHERE car_id = ? ORDER BY date_creation DESC'); $stmtNotes->execute([$carId]); sendJson(200, [ 'success' => true, 'consulted_by' => $currentUser['username'], 'data' => [ 'voiture' => $car, 'maintenances' => $stmtMaint->fetchAll(), 'notes' => $stmtNotes->fetchAll() ] ]); } } // 2. /cars/{id}/kilometrage if ($carId && $subResource === 'kilometrage') { // GET: Public if ($method === 'GET') { $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 50; try { $stmt = $db->prepare('CALL sp_getKilometrages(:id, :limit)'); $stmt->execute([':id' => $carId, ':limit' => $limit]); $data = $stmt->fetchAll(); $stmt->closeCursor(); } catch (Exception $e) { $stmt = $db->prepare('SELECT * FROM kilometrage WHERE car_id = ? ORDER BY date_releve DESC, id DESC LIMIT ?'); $stmt->bindValue(1, $carId, PDO::PARAM_INT); $stmt->bindValue(2, $limit, PDO::PARAM_INT); $stmt->execute(); $data = $stmt->fetchAll(); } sendJson(200, ['success' => true, 'data' => $data]); } // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { authenticate($db, true); $body = getBody(); if (!isset($body['valeur'])) { sendJson(400, ['success' => false, 'error' => 'Le champ valeur (kilométrage) est obligatoire']); } $dateReleve = $body['date_releve'] ?? date('Y-m-d'); $valeur = (int)$body['valeur']; $stmt = $db->prepare('INSERT INTO kilometrage (car_id, date_releve, valeur) VALUES (?, ?, ?)'); $stmt->execute([$carId, $dateReleve, $valeur]); $newId = (int)$db->lastInsertId(); sendJson(201, [ 'success' => true, 'message' => 'Relevé kilométrique enregistré', 'data' => [ 'id' => $newId, 'car_id' => $carId, 'date_releve' => $dateReleve, 'valeur' => $valeur ] ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // 3. /cars/{id}/maintenance if ($carId && ($subResource === 'maintenance' || $subResource === 'entretiens')) { // GET: Public if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE car_id = ? ORDER BY date_evenement DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { authenticate($db, true); $body = getBody(); if (empty($body['type_entretien']) || empty($body['date_evenement']) || !isset($body['kilometrage'])) { sendJson(400, [ 'success' => false, 'error' => 'Les champs type_entretien, date_evenement et kilometrage sont obligatoires' ]); } $stmt = $db->prepare(' INSERT INTO maintenance_logs (car_id, type_entretien, date_evenement, kilometrage, description, prix) VALUES (?, ?, ?, ?, ?, ?) '); $stmt->execute([ $carId, trim((string)$body['type_entretien']), (string)$body['date_evenement'], (int)$body['kilometrage'], isset($body['description']) ? trim((string)$body['description']) : null, isset($body['prix']) ? (float)$body['prix'] : null ]); $newId = (int)$db->lastInsertId(); $stmtGet = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Entretien enregistré avec succès', 'data' => $stmtGet->fetch() ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // 4. /cars/{id}/notes [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] if ($carId && $subResource === 'notes') { // GET: Protégé (Notes de suivi confidentielles) if ($method === 'GET') { authenticate($db, true); $stmt = $db->prepare('SELECT * FROM notes WHERE car_id = ? ORDER BY date_creation DESC'); $stmt->execute([$carId]); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } // POST: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'POST') { authenticate($db, true); $body = getBody(); if (empty($body['contenu'])) { sendJson(400, ['success' => false, 'error' => 'Le champ contenu est obligatoire']); } $titre = isset($body['titre']) ? trim((string)$body['titre']) : null; $contenu = trim((string)$body['contenu']); $stmt = $db->prepare('INSERT INTO notes (car_id, titre, contenu) VALUES (?, ?, ?)'); $stmt->execute([$carId, $titre, $contenu]); $newId = (int)$db->lastInsertId(); $stmtGet = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Note enregistrée avec succès', 'data' => $stmtGet->fetch() ]); } sendJson(405, ['success' => false, 'error' => 'Méthode non autorisée pour cette sous-ressource']); } // --- ACTIONS PRINCIPALES SUR LES VOITURES --- // GET /cars : Liste toutes les voitures (Public) if ($carId === null && $subResource === null && $method === 'GET') { if (!empty($_GET['immatriculation'])) { findCarByPlate($db, (string)$_GET['immatriculation']); } $sql = 'SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c ORDER BY c.id ASC'; $stmt = $db->query($sql); sendJson(200, ['success' => true, 'data' => $stmt->fetchAll()]); } // GET /cars/{id} : Consulter une voiture (Public) if ($carId !== null && $subResource === null && $method === 'GET') { $stmt = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmt->execute([$carId]); $car = $stmt->fetch(); if (!$car) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $car]); } // POST /cars : Créer une voiture [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($carId === null && $method === 'POST') { authenticate($db, true); $body = getBody(); $required = ['marque', 'modele', 'annee', 'dateAchat']; foreach ($required as $field) { if (empty($body[$field])) { sendJson(400, ['success' => false, 'error' => "Le champ '$field' est obligatoire"]); } } $marque = trim((string)$body['marque']); $modele = trim((string)$body['modele']); $annee = (int)$body['annee']; $dateAchat = (string)$body['dateAchat']; $vin = !empty($body['VIN']) ? trim((string)$body['VIN']) : null; $immatriculation = !empty($body['immatriculation']) ? strtoupper(trim((string)$body['immatriculation'])) : null; // Vérifier unicité de l'immatriculation if ($immatriculation !== null) { $normImmat = str_replace(['-', ' ', '.', '_'], '', $immatriculation); $stmtCheck = $db->prepare(' SELECT id FROM cars WHERE UPPER(immatriculation) = :raw OR REPLACE(REPLACE(REPLACE(REPLACE(UPPER(immatriculation), "-", ""), " ", ""), ".", ""), "_", "") = :norm '); $stmtCheck->execute([':raw' => $immatriculation, ':norm' => $normImmat]); if ($stmtCheck->fetch()) { sendJson(409, [ 'success' => false, 'error' => "L'immatriculation '$immatriculation' est déjà utilisée pour un autre véhicule." ]); } } // Appel de la procédure stockée sp_insertVoiture try { $stmt = $db->prepare('CALL sp_insertVoiture(:marque, :modele, :annee, :dateAchat, :vin, :immatriculation)'); $stmt->execute([ ':marque' => $marque, ':modele' => $modele, ':annee' => $annee, ':dateAchat' => $dateAchat, ':vin' => $vin, ':immatriculation' => $immatriculation ]); $res = $stmt->fetch(); $stmt->closeCursor(); $newId = isset($res['new_id']) ? (int)$res['new_id'] : (int)$db->lastInsertId(); } catch (Exception $e) { $stmt = $db->prepare('INSERT INTO cars (marque, modele, annee, dateAchat, VIN, immatriculation) VALUES (?, ?, ?, ?, ?, ?)'); $stmt->execute([$marque, $modele, $annee, $dateAchat, $vin, $immatriculation]); $newId = (int)$db->lastInsertId(); } if (!empty($body['kilometrage_initial'])) { $kmStmt = $db->prepare('INSERT INTO kilometrage (car_id, date_releve, valeur) VALUES (?, ?, ?)'); $kmStmt->execute([$newId, $dateAchat, (int)$body['kilometrage_initial']]); } $stmtGet = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtGet->execute([$newId]); sendJson(201, [ 'success' => true, 'message' => 'Voiture créée avec succès', 'data' => $stmtGet->fetch() ]); } // PUT / PATCH /cars/{id} : Mettre à jour les paramètres [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($carId !== null && $subResource === null && ($method === 'PUT' || $method === 'PATCH')) { authenticate($db, true); $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } $body = getBody(); $allowedFields = ['marque', 'modele', 'annee', 'dateAchat', 'VIN', 'immatriculation']; $updates = []; $params = []; if (!empty($body['immatriculation'])) { $immat = strtoupper(trim((string)$body['immatriculation'])); $body['immatriculation'] = $immat; $normImmat = str_replace(['-', ' ', '.', '_'], '', $immat); $stmtCheck = $db->prepare(' SELECT id FROM cars WHERE (UPPER(immatriculation) = :raw OR REPLACE(REPLACE(REPLACE(REPLACE(UPPER(immatriculation), "-", ""), " ", ""), ".", ""), "_", "") = :norm) AND id != :id '); $stmtCheck->execute([':raw' => $immat, ':norm' => $normImmat, ':id' => $carId]); if ($stmtCheck->fetch()) { sendJson(409, [ 'success' => false, 'error' => "L'immatriculation '$immat' est déjà utilisée pour un autre véhicule." ]); } } foreach ($allowedFields as $field) { if (array_key_exists($field, $body)) { $updates[] = "`$field` = ?"; $params[] = $body[$field]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre valide à mettre à jour']); } $params[] = $carId; $sql = 'UPDATE cars SET ' . implode(', ', $updates) . ' WHERE id = ?'; $stmt = $db->prepare($sql); $stmt->execute($params); $stmtGet = $db->prepare(' SELECT c.*, (SELECT valeur FROM kilometrage WHERE car_id = c.id ORDER BY date_releve DESC, id DESC LIMIT 1) AS dernier_km FROM cars c WHERE c.id = ? '); $stmtGet->execute([$carId]); sendJson(200, [ 'success' => true, 'message' => 'Paramètres de la voiture mis à jour avec succès', 'data' => $stmtGet->fetch() ]); } // DELETE /cars/{id} : Supprimer une voiture [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($carId !== null && $subResource === null && $method === 'DELETE') { authenticate($db, true, 'admin'); $check = $db->prepare('SELECT id FROM cars WHERE id = ?'); $check->execute([$carId]); if (!$check->fetch()) { sendJson(404, ['success' => false, 'error' => "Voiture #{$carId} introuvable"]); } try { $stmt = $db->prepare('CALL sp_deleteVoiture(:id)'); $stmt->execute([':id' => $carId]); $stmt->closeCursor(); } catch (Exception $e) { $stmt = $db->prepare('DELETE FROM cars WHERE id = ?'); $stmt->execute([$carId]); } sendJson(200, [ 'success' => true, 'message' => "Voiture #{$carId} ainsi que toutes ses données associées ont été supprimées avec succès" ]); } sendJson(405, ['success' => false, 'error' => "Méthode HTTP non autorisée pour cette URL"]); } // ========================================================================= // RESSOURCE: KILOMETRAGE DIRECT (/kilometrage/{id}) // ========================================================================= if ($resource === 'kilometrage') { $kmId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; // DELETE: [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($kmId && $method === 'DELETE') { authenticate($db, true, 'admin'); $stmt = $db->prepare('DELETE FROM kilometrage WHERE id = ?'); $stmt->execute([$kmId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Relevé kilométrique #{$kmId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Relevé kilométrique #{$kmId} supprimé"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /kilometrage"]); } // ========================================================================= // RESSOURCE: ENTRETIENS DIRECT (/maintenance/{id}) // ========================================================================= if ($resource === 'maintenance' || $resource === 'entretiens') { $maintId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; if (!$maintId) { sendJson(400, ['success' => false, 'error' => 'Identifiant d\'entretien requis']); } // GET: Public if ($method === 'GET') { $stmt = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); $item = $stmt->fetch(); if (!$item) { sendJson(404, ['success' => false, 'error' => "Entretien #{$maintId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $item]); } // PUT/PATCH: [ACTION PROTÉGÉE - AUTHENTIFICATION REQUISE] if ($method === 'PUT' || $method === 'PATCH') { authenticate($db, true); $body = getBody(); $allowed = ['type_entretien', 'date_evenement', 'kilometrage', 'description', 'prix']; $updates = []; $params = []; foreach ($allowed as $f) { if (array_key_exists($f, $body)) { $updates[] = "`$f` = ?"; $params[] = $body[$f]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre à modifier']); } $params[] = $maintId; $stmt = $db->prepare('UPDATE maintenance_logs SET ' . implode(', ', $updates) . ' WHERE id = ?'); $stmt->execute($params); $stmtGet = $db->prepare('SELECT * FROM maintenance_logs WHERE id = ?'); $stmtGet->execute([$maintId]); sendJson(200, [ 'success' => true, 'message' => 'Entretien mis à jour avec succès', 'data' => $stmtGet->fetch() ]); } // DELETE: [ACTION PROTÉGÉE - RÔLE ADMIN REQUIS] if ($method === 'DELETE') { authenticate($db, true, 'admin'); $stmt = $db->prepare('DELETE FROM maintenance_logs WHERE id = ?'); $stmt->execute([$maintId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Entretien #{$maintId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Entretien #{$maintId} supprimé avec succès"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /maintenance/{id}"]); } // ========================================================================= // RESSOURCE: NOTES DIRECT (/notes/{id}) [CONTENU RESTREINT - AUTHENTIFICATION REQUISE] // ========================================================================= if ($resource === 'notes') { $noteId = isset($segments[1]) && is_numeric($segments[1]) ? (int)$segments[1] : null; if (!$noteId) { sendJson(400, ['success' => false, 'error' => 'Identifiant de note requis']); } // GET: Authentifié if ($method === 'GET') { authenticate($db, true); $stmt = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmt->execute([$noteId]); $item = $stmt->fetch(); if (!$item) { sendJson(404, ['success' => false, 'error' => "Note #{$noteId} introuvable"]); } sendJson(200, ['success' => true, 'data' => $item]); } // PUT/PATCH: Authentifié if ($method === 'PUT' || $method === 'PATCH') { authenticate($db, true); $body = getBody(); $allowed = ['titre', 'contenu']; $updates = []; $params = []; foreach ($allowed as $f) { if (array_key_exists($f, $body)) { $updates[] = "`$f` = ?"; $params[] = $body[$f]; } } if (empty($updates)) { sendJson(400, ['success' => false, 'error' => 'Aucun paramètre à modifier']); } $params[] = $noteId; $stmt = $db->prepare('UPDATE notes SET ' . implode(', ', $updates) . ' WHERE id = ?'); $stmt->execute($params); $stmtGet = $db->prepare('SELECT * FROM notes WHERE id = ?'); $stmtGet->execute([$noteId]); sendJson(200, [ 'success' => true, 'message' => 'Note mise à jour avec succès', 'data' => $stmtGet->fetch() ]); } // DELETE: Authentifié if ($method === 'DELETE') { authenticate($db, true); $stmt = $db->prepare('DELETE FROM notes WHERE id = ?'); $stmt->execute([$noteId]); if ($stmt->rowCount() === 0) { sendJson(404, ['success' => false, 'error' => "Note #{$noteId} introuvable"]); } sendJson(200, ['success' => true, 'message' => "Note #{$noteId} supprimée avec succès"]); } sendJson(405, ['success' => false, 'error' => "Méthode non autorisée sur /notes/{id}"]); } sendJson(404, [ 'success' => false, 'error' => "Ressource '/$resource' non reconnue. Consultez la racine '/' pour la documentation des routes." ]); } catch (PDOException $e) { sendJson(500, ['success' => false, 'error' => 'Erreur SQL: ' . $e->getMessage()]); } catch (Throwable $e) { sendJson(500, ['success' => false, 'error' => 'Erreur serveur: ' . $e->getMessage()]); }