7200, 'path' => '/', 'domain' => $_SERVER['HTTP_HOST'] ?? '', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]); session_start(); require_once dirname(__DIR__, 2) . '/app_core/config/database.php'; require_once dirname(__DIR__, 2) . '/app_core/controllers/AuthController.php'; use App\Config\Database; use App\Controllers\AuthController; header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: SAMEORIGIN'); header('X-XSS-Protection: 1; mode=block'); header('Referrer-Policy: strict-origin-when-cross-origin'); \(route =\)_GET['route'] ?? ''; \(route = trim(\)route, '/'); \(method =\)_SERVER['REQUEST_METHOD'] ?? 'GET'; try { $db = Database::getConnection(); // ============================================================================== // 2. AUTO-CIERRE JUST-IN-TIME (EXÁMENES HUÉRFANOS > 200s SIN SEÑAL) // ============================================================================== \(sweepStmt =\)db->prepare(" UPDATE intentos_evaluacion SET estado = CASE WHEN NOW() > hora_limite THEN 'EXPIRADO' ELSE 'ABANDONADO' END, hora_finalizacion = NOW(), calificacion_obtenida = 0.00, aprobado = 0 WHERE estado = 'EN_CURSO' AND TIMESTAMPDIFF(SECOND, ultimo_latido, NOW()) > 200 "); $sweepStmt->execute(); // ============================================================================== // 3. API REST // ============================================================================== if (str_starts_with($route, 'api/')) { header('Content-Type: application/json; charset=utf-8'); // Comprobación de Salud del Servidor if ($route === 'api/health') { echo json_encode([ 'status' => 'ok', 'database' => 'connected', 'system' => 'SGA Caldas v1.0', 'subdomain' => 'gestiondeaprendizajecaldas.mesadeayudahsvp.com', 'timestamp' => date('c') ]); exit; } // Listar Cursos Activos if (\(route === 'api/cursos' &&\)method === 'GET') { \(stmt =\)db->query("SELECT id, titulo, categoria, porcentaje_minimo FROM cursos WHERE activo = 1"); echo json_encode(['data' => $stmt->fetchAll()]); exit; } // Login if (\(route === 'api/auth/login' &&\)method === 'POST') {$input = json_decode(file_get_contents('php://input'), true) ?? []; AuthController::login(\(db,\)input); exit; } // Registro if (\(route === 'api/auth/registro' &&\)method === 'POST') {$input = json_decode(file_get_contents('php://input'), true) ?? []; AuthController::registro(\(db,\)input); exit; } // Logout if (\(route === 'api/auth/logout' &&\)method === 'POST') { AuthController::logout(); exit; } // Verificación de Certificados if (str_starts_with(\(route, 'api/certificados/validar/') &&\)method === 'GET') { \(codigo = basename(\)route); \(stmt =\)db->prepare(" SELECT c.codigo_unico, c.calificacion_final, c.fecha_emision, c.hash_criptografico, u.nombres, u.apellidos, u.tipo_documento, CONCAT(SUBSTRING(u.numero_documento, 1, 3), '****', SUBSTRING(u.numero_documento, -3)) as documento_protegido, cur.titulo as curso_titulo FROM certificados c JOIN usuarios u ON u.id = c.usuario_id JOIN cursos cur ON cur.id = c.curso_id WHERE c.codigo_unico = :codigo AND c.activo = 1 "); \(stmt->execute(['codigo' =>\)codigo]); \(cert =\)stmt->fetch(); if (!$cert) { http_response_code(404); echo json_encode(['valido' => false, 'mensaje' => 'Certificado no encontrado o revocado.']); exit; } echo json_encode([ 'valido' => true, 'codigo' => $cert['codigo_unico'], 'estudiante' => \(cert['nombres'] . ' ' .\)cert['apellidos'], 'documentoOculto' => \(cert['tipo_documento'] . ' ' .\)cert['documento_protegido'], 'curso' => $cert['curso_titulo'], 'calificacion' => (float)$cert['calificacion_final'], 'fechaEmision' => $cert['fecha_emision'], 'hash' => $cert['hash_criptografico'] ]); exit; } http_response_code(404); echo json_encode(['error' => 'Endpoint no encontrado', 'ruta' => $route]); exit; } // ============================================================================== // 4. VISTA WEB OFICIAL // ============================================================================== renderWebShell(); } catch (\Throwable $e) { error_log('[SGA Error Fatal] ' . $e->getMessage()); if (str_starts_with($route, 'api/')) { header('Content-Type: application/json; charset=utf-8'); http_response_code(500); echo json_encode(['error' => 'Error interno en la plataforma.']); exit; } http_response_code(500); echo "