Primo rilascio
This commit is contained in:
229
backend/public/index.php
Normal file
229
backend/public/index.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Database;
|
||||
use App\Controllers\AuthController;
|
||||
use App\Controllers\ChildController;
|
||||
use App\Controllers\SchoolYearController;
|
||||
use App\Controllers\ShiftDefinitionController;
|
||||
use App\Controllers\StructureController;
|
||||
use App\Controllers\TeacherContractController;
|
||||
use App\Controllers\TeacherController;
|
||||
use Firebase\JWT\JWT; // Importa JWT
|
||||
use Firebase\JWT\Key; // Importa Key
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\SignatureInvalidException;
|
||||
|
||||
// 1. Carica Autoloader Composer
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 2. Carica Variabili d'Ambiente
|
||||
try {
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
|
||||
$dotenv->load();
|
||||
} catch (\Throwable $th) {
|
||||
error_log("Could not load .env file: " . $th->getMessage());
|
||||
}
|
||||
|
||||
// --- Configurazione JWT ---
|
||||
$jwtSecret = $_ENV['JWT_SECRET'] ?? 'default-secret-change-me';
|
||||
|
||||
// --- Funzione Guardia JWT ---
|
||||
/**
|
||||
* Verifica il token JWT dall'header Authorization.
|
||||
* Restituisce i dati utente dal payload se valido, altrimenti termina con errore 401.
|
||||
* @return array Dati utente dal payload JWT ('sub', 'data')
|
||||
*/
|
||||
function authenticate(): array {
|
||||
global $jwtSecret; // Rende visibile la chiave segreta definita sopra
|
||||
|
||||
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? null;
|
||||
|
||||
if (!$authHeader) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Authorization header missing']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Estrai il token "Bearer <token>"
|
||||
if (!preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Malformed Authorization header']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$jwt = $matches[1];
|
||||
if (!$jwt) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Access token missing']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$decoded = JWT::decode($jwt, new Key($jwtSecret, 'HS256'));
|
||||
// Restituisce l'intero payload decodificato come array associativo
|
||||
return (array)$decoded;
|
||||
} catch (ExpiredException $e) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Token expired']);
|
||||
exit;
|
||||
} catch (SignatureInvalidException $e) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Invalid token signature']);
|
||||
exit;
|
||||
} catch (\Throwable $e) { // Cattura altri errori JWT
|
||||
http_response_code(401);
|
||||
error_log("JWT Decode Error: " . $e->getMessage());
|
||||
echo json_encode(['error' => 'Invalid token']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3. Imposta Headers CORS
|
||||
$allowedOrigin = $_ENV['FRONTEND_URL'] ?? 'http://localhost:4200';
|
||||
header("Access-Control-Allow-Origin: " . $allowedOrigin);
|
||||
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
|
||||
header("Access-Control-Allow-Credentials: true");
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204); exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// 4. Definizione Route
|
||||
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
|
||||
|
||||
// --- Public Routes ---
|
||||
$r->addRoute('POST', '/api/login', [AuthController::class, 'login']);
|
||||
$r->addRoute('GET', '/api/ping', function () { echo json_encode(['message' => 'pong']); });
|
||||
|
||||
// --- Protected Routes (richiedono autenticazione) ---
|
||||
|
||||
// Aggiungiamo un gruppo per applicare la guardia a più route
|
||||
$r->addGroup('/api', function (FastRoute\RouteCollector $r) {
|
||||
// La guardia verrà chiamata prima dell'handler per queste route
|
||||
|
||||
// Route /api/me (protetta)
|
||||
$r->addRoute('GET', '/me', [AuthController::class, 'me']);
|
||||
|
||||
// Structures
|
||||
$r->addRoute('GET', '/structures', [StructureController::class, 'index']);
|
||||
$r->addRoute('POST', '/structures', [StructureController::class, 'store']);
|
||||
$r->addRoute('GET', '/structures/{id:\d+}', [StructureController::class, 'show']);
|
||||
$r->addRoute('PUT', '/structures/{id:\d+}', [StructureController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/structures/{id:\d+}', [StructureController::class, 'delete']);
|
||||
|
||||
// Teachers
|
||||
$r->addRoute('GET', '/teachers', [TeacherController::class, 'index']);
|
||||
$r->addRoute('POST', '/teachers', [TeacherController::class, 'store']);
|
||||
$r->addRoute('GET', '/teachers/{id:\d+}', [TeacherController::class, 'show']);
|
||||
$r->addRoute('PUT', '/teachers/{id:\d+}', [TeacherController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/teachers/{id:\d+}', [TeacherController::class, 'delete']);
|
||||
|
||||
// School Years
|
||||
$r->addRoute('GET', '/school-years', [SchoolYearController::class, 'index']);
|
||||
$r->addRoute('POST', '/school-years', [SchoolYearController::class, 'store']);
|
||||
$r->addRoute('GET', '/school-years/{id:\d+}', [SchoolYearController::class, 'show']);
|
||||
$r->addRoute('PUT', '/school-years/{id:\d+}', [SchoolYearController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/school-years/{id:\d+}', [SchoolYearController::class, 'delete']);
|
||||
|
||||
// Teacher Contracts
|
||||
$r->addRoute('GET', '/teacher-contracts', [TeacherContractController::class, 'index']);
|
||||
$r->addRoute('POST', '/teacher-contracts', [TeacherContractController::class, 'store']);
|
||||
$r->addRoute('GET', '/teacher-contracts/{id:\d+}', [TeacherContractController::class, 'show']);
|
||||
$r->addRoute('PUT', '/teacher-contracts/{id:\d+}', [TeacherContractController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/teacher-contracts/{id:\d+}', [TeacherContractController::class, 'delete']);
|
||||
|
||||
// Children
|
||||
$r->addRoute('GET', '/children', [ChildController::class, 'index']);
|
||||
$r->addRoute('POST', '/children', [ChildController::class, 'store']);
|
||||
$r->addRoute('GET', '/children/{id:\d+}', [ChildController::class, 'show']);
|
||||
$r->addRoute('PUT', '/children/{id:\d+}', [ChildController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/children/{id:\d+}', [ChildController::class, 'delete']);
|
||||
|
||||
// Shift Definitions
|
||||
$r->addRoute('GET', '/shift-definitions', [ShiftDefinitionController::class, 'index']);
|
||||
$r->addRoute('POST', '/shift-definitions', [ShiftDefinitionController::class, 'store']);
|
||||
$r->addRoute('GET', '/shift-definitions/{id:\d+}', [ShiftDefinitionController::class, 'show']);
|
||||
$r->addRoute('PUT', '/shift-definitions/{id:\d+}', [ShiftDefinitionController::class, 'update']);
|
||||
$r->addRoute('DELETE', '/shift-definitions/{id:\d+}', [ShiftDefinitionController::class, 'delete']);
|
||||
|
||||
}); // Fine gruppo /api
|
||||
|
||||
});
|
||||
|
||||
// 5. Gestione della Richiesta
|
||||
$httpMethod = $_SERVER['REQUEST_METHOD'];
|
||||
$uri = $_SERVER['REQUEST_URI'];
|
||||
|
||||
if (false !== $pos = strpos($uri, '?')) {
|
||||
$uri = substr($uri, 0, $pos);
|
||||
}
|
||||
$uri = rawurldecode($uri);
|
||||
|
||||
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
|
||||
$userData = null; // Inizializza a null
|
||||
|
||||
switch ($routeInfo[0]) {
|
||||
case FastRoute\Dispatcher::NOT_FOUND:
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'API Endpoint Not Found']);
|
||||
break;
|
||||
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
|
||||
$allowedMethods = $routeInfo[1];
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method Not Allowed', 'allowed_methods' => $allowedMethods]);
|
||||
break;
|
||||
case FastRoute\Dispatcher::FOUND:
|
||||
$handler = $routeInfo[1];
|
||||
$vars = $routeInfo[2];
|
||||
|
||||
// --- Applica Guardia JWT se la route è protetta (inizia con /api/ e non è /api/login o /api/ping) ---
|
||||
if (strpos($uri, '/api/') === 0 && !in_array($uri, ['/api/login', '/api/ping'])) {
|
||||
try {
|
||||
$userData = authenticate(); // Verifica token e ottieni dati utente
|
||||
// Potresti aggiungere qui controllo del ruolo se necessario per specifiche route
|
||||
// if ($userData['data']->role !== 'admin' && $uri === '/api/users') { ... }
|
||||
} catch (\Exception $e) {
|
||||
// L'eccezione è già gestita dentro authenticate() con exit
|
||||
// Questo blocco catch è per sicurezza, ma non dovrebbe essere raggiunto
|
||||
http_response_code(500);
|
||||
error_log("Unexpected error during authentication check: " . $e->getMessage());
|
||||
echo json_encode(['error' => 'Authentication check failed']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Esecuzione Handler ---
|
||||
try {
|
||||
if (is_callable($handler)) {
|
||||
call_user_func($handler, $vars); // Passa $vars alla closure
|
||||
} elseif (is_array($handler) && count($handler) === 2 && is_string($handler[0]) && is_string($handler[1])) {
|
||||
[$class, $method] = $handler;
|
||||
if (class_exists($class) && method_exists($class, $method)) {
|
||||
$controller = new $class();
|
||||
// Passa $vars e $userData (se presente) al metodo del controller
|
||||
// Modifica i metodi del controller per accettare $userData opzionale se serve
|
||||
if ($method === 'me' && $userData !== null) { // Caso speciale per AuthController::me
|
||||
call_user_func([$controller, $method], $userData['data']); // Passa solo il payload 'data'
|
||||
} else {
|
||||
call_user_func([$controller, $method], $vars); // Chiamata standard per gli altri
|
||||
}
|
||||
|
||||
} else {
|
||||
http_response_code(500); error_log("Handler class/method not found: {$class}::{$method}"); echo json_encode(['error' => 'Internal Server Error']);
|
||||
}
|
||||
} else {
|
||||
http_response_code(500); error_log("Invalid handler type defined."); echo json_encode(['error' => 'Internal Server Error']);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
http_response_code(500); error_log("Unhandled error in route handler: " . $e->getMessage() . "\n" . $e->getTraceAsString()); echo json_encode(['error' => 'Internal Server Error']);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
http_response_code(500); echo json_encode(['error' => 'Internal Server Error - Dispatcher Error']); break;
|
||||
}
|
||||
10
backend/public/php_errors.log
Normal file
10
backend/public/php_errors.log
Normal file
@@ -0,0 +1,10 @@
|
||||
[06-Mar-2026 21:29:32 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:29:32 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:29:56 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:29:56 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:41 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:41 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:41 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:41 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:50 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
[06-Mar-2026 21:30:50 UTC] PHP Warning: Array to string conversion in D:\Works\NidoAi\backend\public\index.php on line 90
|
||||
Reference in New Issue
Block a user