From 8264459a6b56ea88eec0b4f1460345b2a4bbd75e Mon Sep 17 00:00:00 2001 From: lgromero Date: Fri, 18 Oct 2024 06:24:36 +0000 Subject: [PATCH] refs #949 #943 #969 fix Command output return, add Command in the services.yaml, adds url to the oglives downloables --- bin/oglivecli | 5 +- config/services.yaml | 2 +- .../Command/OgLiveInstallCommand.php | 158 ++++ .../Controller/OgBootController.php | 858 +++++++++++------- 4 files changed, 692 insertions(+), 331 deletions(-) create mode 100644 src/OgBootBundle/Command/OgLiveInstallCommand.php diff --git a/bin/oglivecli b/bin/oglivecli index 21bc293..b2e6e24 100755 --- a/bin/oglivecli +++ b/bin/oglivecli @@ -205,13 +205,14 @@ function downloadMenu() { fi ISOREL=${OGLIVE[i-1]##*-r}; ISOREL=${ISOREL%%.*} [ $ISOREL -ge $MINREL ] && compatible=true - + url="$DOWNLOADURL/${OGLIVE[i-1]}" local DATA=$(jq -n \ --arg id "$i" \ --arg filename "${OGLIVE[i-1]}" \ + --arg url "$url" \ --argjson installed "$installed" \ --argjson compatible "$compatible" \ - '{id: $id, filename: $filename, installed: $installed, compatible: $compatible}') + '{id: $id, filename: $filename, url: $url, installed: $installed, compatible: $compatible}') downloads+=("$DATA") done diff --git a/config/services.yaml b/config/services.yaml index a3ed921..8c115cf 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -26,5 +26,5 @@ services: resource: '../src/OgBootBundle/Controller' tags: ['controller.service_arguments'] # Register the OgLiveInstallCommand explicitly to ensure it's detected - OgBootBundle\Command\OgLiveInstallCommand: + App\OgBootBundle\Command\OgLiveInstallCommand: tags: ['console.command'] diff --git a/src/OgBootBundle/Command/OgLiveInstallCommand.php b/src/OgBootBundle/Command/OgLiveInstallCommand.php new file mode 100644 index 0000000..20ae2b7 --- /dev/null +++ b/src/OgBootBundle/Command/OgLiveInstallCommand.php @@ -0,0 +1,158 @@ +curlRequestService = $curlRequestService; + #$this->httpClient = $httpClient; + $this->httpClient = HttpClient::create([ + 'verify_peer' => false, // Ignorar la verificación del certificado SSL + 'verify_host' => false, // Ignorar la verificación del nombre del host + ]); + $this->params = $params; + $this->logger = $logger; // Añadimos el logger + } + + protected function configure() + { + $this + ->setDescription('Instala un ogLive en segundo plano') + ->addArgument('isoUrl', InputArgument::REQUIRED, 'URL del ISO de ogLive') + ->addArgument('transactionId', InputArgument::REQUIRED, 'ID de la transacción'); + } + +protected function execute(InputInterface $input, OutputInterface $output) +{ + ini_set('memory_limit', '-1'); + ini_set('max_execution_time', '3000'); + $isoUrl = $input->getArgument('isoUrl'); + + $transactionId = $input->getArgument('transactionId'); + + // Iniciamos los logs para rastrear el proceso + $this->logger->info('Starting ogLive installation', ['transactionId' => $transactionId]); + + try { + // Log: comenzando la descarga del ISO + $this->logger->info('Starting download', ['isoUrl' => $isoUrl]); + + // Llamar al servicio para iniciar la instalación + $installResult = $this->curlRequestService->callOgLive("download " . escapeshellarg($isoUrl)); + + // Log: descarga finalizada + $this->logger->info('Download finished', ['transactionId' => $transactionId, 'installResult' => $installResult]); + + // Verificar el resultado de la instalación basado en el output del comando + $status = 'success'; + $messageText = 'ogLive client installed successfully'; + $exitCode = $installResult['exitCode']; + + if (isset($installResult['output']['error'])) { + // Si hay un error, asignar el estado y mensaje adecuado + $status = $installResult['output']['error']; + $messageText = $installResult['output']['message']; + + // Log: la instalación falló + $this->logger->error('Installation failed', ['transactionId' => $transactionId, 'error' => $messageText]); + } elseif ($installResult['exitCode'] !== 0) { + // Si hubo un exitCode distinto de 0, manejar como error desconocido + $status = 'UNKNOWN_ERROR'; + $messageText = 'An unknown error occurred during the installation process.'; + $this->logger->error('Unknown installation error', ['transactionId' => $transactionId, 'exitCode' => $installResult['exitCode']]); + } else { + // Log: instalación completada con éxito + $messageText = $installResult['output']['message']; + $this->logger->info('Installation completed successfully', ['transactionId' => $transactionId]); + } + + // Preparar los datos para el webhook según el resultado + $webhookData = [ + 'ogCoreId' => $transactionId, + 'status' => $status, + 'code' => ($exitCode === 0) ? 200 : $exitCode, // Cambiar a 200 si es éxito + 'message' => $messageText , + ]; + + $this->logger->info('Installation completed with details', ['installResult' => $installResult]); + $this->logger->info('Webhook data to be sent: ', ['webhookData' => $webhookData]); + + // Obtener la URL del webhook desde el archivo .env + $webhookUrl = "https://172.17.8.90:8443/og-lives/install/webhook"; + + // Log: enviando datos al webhook + $this->logger->info('Sending data to webhook', ['webhookUrl' => $webhookUrl, 'webhookData' => $webhookData]); + + // Llamar al webhook para notificar el resultado + $this->notifyWebhook($webhookUrl, $webhookData); + $this->logger->info('Notify webhook con exito'); + // Log: notificación al webhook finalizada + $this->logger->info('Webhook notification sent', ['transactionId' => $transactionId]); + + } catch (Exception $e) { + // Log: error en la instalación + $this->logger->error('Failed to complete ogLive installation', ['transactionId' => $transactionId, 'exception' => $e->getMessage()]); + + // Manejar errores y enviar notificación de fallo al webhook + $webhookData = [ + 'ogCoreId' => $transactionId, + 'status' => 'failure', + 'code' => 500, + 'message' => $e->getMessage() + ]; + + $webhookUrl = "https://172.17.8.90:8443/og-lives/install/webhook"; + $this->notifyWebhook($webhookUrl, $webhookData); + $this->logger->info('Notify webhook terminado con errores'); + // Log: notificación de error enviada al webhook + $this->logger->info('Failure notification sent to webhook', ['transactionId' => $transactionId]); + } + + // Log: proceso de instalación terminado + $this->logger->info('Installation process ended', ['transactionId' => $transactionId]); + + return Command::SUCCESS; +} + + /** + * Enviar el resultado al webhook + */ + private function notifyWebhook(string $webhookUrl, array $webhookData): void + { + try { + $this->logger->info('Enter notify webhook'); + $this->logger->info('Data to be sent', ['webhookData' => $webhookData]); + $this->httpClient->request('POST', $webhookUrl, [ + 'headers' => [ + 'accept' => 'application/json', + 'Content-Type' => 'application/json', + ], + 'body' => json_encode(['webhookData' => $webhookData]), + ]); + + // Log: éxito al enviar la notificación + $this->logger->info('Webhook data sent successfully', ['webhookUrl' => $webhookUrl, 'webhookData' => $webhookData]); + } catch (Exception $e) { + // Log: error al enviar al webhook + $this->logger->error('Error sending webhook notification', ['webhookUrl' => $webhookUrl, 'exception' => $e->getMessage()]); + } + } +} + diff --git a/src/OgBootBundle/Controller/OgBootController.php b/src/OgBootBundle/Controller/OgBootController.php index 528eb57..4d435fe 100644 --- a/src/OgBootBundle/Controller/OgBootController.php +++ b/src/OgBootBundle/Controller/OgBootController.php @@ -168,13 +168,15 @@ public function getStatus(): Response ); } - // Formatear la respuesta final con todos los datos $response = [ - 'disk_usage' => $diskUsageResult, - 'default_oglive' => $defaultOglive, - 'installed_oglives' => $installedOglives, - 'services_status' => $servicesStatusResult - ]; + 'success' => 'Status retrieved successfully', + 'message' => [ + 'disk_usage' => $diskUsageResult, + 'default_oglive' => $defaultOglive, + 'installed_oglives' => $installedOglives, + 'services_status' => $servicesStatusResult + ] + ]; // Formatear la respuesta final con todos los datos return new JsonResponse($response, Response::HTTP_OK); } @@ -319,7 +321,6 @@ public function getOglives(): Response return new JsonResponse($response, Response::HTTP_OK); } - /** * @Route("/ogboot/v1/oglives/default", name="getOgliveDefault", methods={"GET"}) * @OA\Get( @@ -330,27 +331,41 @@ public function getOglives(): Response * description="Successful operation", * @OA\JsonContent( * type="object", - * @OA\Property(property="success", type="string", example="Se han obtenido la lista de oglives instalados correctamente"), + * @OA\Property(property="success", type="string", example="se ha obtenido el oglive por defecto"), * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( * response=500, - * description="Failed to retrieve information of the default ogLive client" + * description="Failed to retrieve information of the default ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="SERVER_ERROR"), + * @OA\Property(property="message", type="string", example="Failed to retrieve default ogLive.") + * ) * ) * ) */ public function getOgliveDefault(Request $request): Response { - $result = $this->curlRequestService->callOgLive("get_default"); + $response = $this->curlRequestService->callOgLive("get_default"); + $result = $response['output']; + $exitCode = $response['exitCode']; - if (is_array($result) && isset($result['error'])) { - return new JsonResponse(['error' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + // Verificar si hay un error basado en el código de salida + if ($exitCode !== 0) { + return new JsonResponse( + ['error' => 'SERVER_ERROR', 'message' => $result['error'] ?? 'Failed to retrieve default ogLive.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - return new JsonResponse($result, Response::HTTP_OK); + // Devolver la respuesta en el formato solicitado + return new JsonResponse( + ['success' => 'se ha obtenido el oglive por defecto', 'message' => $result], + Response::HTTP_OK + ); } - /** * @Route("/ogboot/v1/oglives/{checksum}", name="getOglive", methods={"GET"}) * @OA\Get( @@ -368,32 +383,61 @@ public function getOgliveDefault(Request $request): Response * ), * @OA\Response( * response=200, - * description="Successful operation", + * description="ogLive client retrieved successfully", * @OA\JsonContent( * type="object", - * @OA\Property(property="success", type="string", example="Se han obtenido la lista de oglives instalados correctamente"), + * @OA\Property(property="success", type="string", example="ogLive client retrieved successfully"), * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( * response=404, - * description="ogLive client not found" + * description="ogLive client not found", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="NOT_FOUND"), + * @OA\Property(property="message", type="string", example="ogLive client with checksum 9e49a085ba74f97a81bdf9b3d0785094 not found.") + * ) + * ), + * @OA\Response( + * response=500, + * description="Failed to retrieve ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="SERVER_ERROR"), + * @OA\Property(property="message", type="string", example="Failed to retrieve ogLive client.") + * ) * ) * ) */ public function getOglive(string $checksum): Response { - $result = $this->curlRequestService->callOgLive("get_info " . escapeshellarg($checksum)); + // Llamar al servicio ogLive para obtener la información del ogLive correspondiente al checksum proporcionado + $response = $this->curlRequestService->callOgLive("get_info " . escapeshellarg($checksum)); + $result = $response['output']; + $exitCode = $response['exitCode']; - if (is_array($result) && isset($result['error'])) { - return new JsonResponse(['error' => $result['error']], Response::HTTP_NOT_FOUND); + // Verificar si hay un error basado en el código de salida + if ($exitCode === 404) { + return new JsonResponse( + ['error' => 'NOT_FOUND', 'message' => "ogLive client with checksum $checksum not found."], + Response::HTTP_NOT_FOUND + ); + } elseif ($exitCode !== 0) { + return new JsonResponse( + ['error' => 'SERVER_ERROR', 'message' => 'Failed to retrieve ogLive client.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - return new JsonResponse($result, Response::HTTP_OK); + // Formatear la respuesta de éxito con el ogLive encontrado + return new JsonResponse( + ['success' => 'ogLive client retrieved successfully', 'message' => $result], + Response::HTTP_OK + ); } - /** * @Route("/ogboot/v1/oglives/default", name="setOgliveDefault", methods={"PUT"}) * @OA\Put( @@ -416,16 +460,27 @@ public function getOglive(string $checksum): Response * description="ogLive client set as default successfully", * @OA\JsonContent( * type="object", - * @OA\Property(property="message", type="string") + * @OA\Property(property="success", type="string", example="ogLive client set as default successfully"), + * @OA\Property(property="message", ref="#/components/schemas/OgLive") * ) * ), * @OA\Response( - * response=404, - * description="ogLive client not found" + * response=400, + * description="Invalid input data", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="INVALID_INPUT"), + * @OA\Property(property="message", type="string", example="Invalid input data: checksum is required.") + * ) * ), * @OA\Response( * response=500, - * description="Failed to set default ogLive client" + * description="Failed to set default ogLive client", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="error", type="string", example="SERVER_ERROR"), + * @OA\Property(property="message", type="string", example="Failed to set ogLive as default.") + * ) * ) * ) */ @@ -433,20 +488,34 @@ public function setOgliveDefault(Request $request): Response { $data = json_decode($request->getContent(), true); + // Validar que el checksum está presente en los datos de entrada if (!isset($data['checksum'])) { - return new JsonResponse(['error' => 'Invalid input data'], Response::HTTP_BAD_REQUEST); + return new JsonResponse( + ['error' => 'INVALID_INPUT', 'message' => 'Invalid input data: checksum is required.'], + Response::HTTP_BAD_REQUEST + ); } $checksum = $data['checksum']; - // Establecer el ogLive como predeterminado utilizando el checksum proporcionado - $result = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); + // Llamar al servicio para establecer el ogLive como predeterminado utilizando el checksum proporcionado + $response = $this->curlRequestService->callOgLive("set_default " . escapeshellarg($checksum)); + $result = $response['output']; + $exitCode = $response['exitCode']; - if (is_array($result) && isset($result['error'])) { - return new JsonResponse(['error' => $result['error']], Response::HTTP_INTERNAL_SERVER_ERROR); + // Verificar si hay un error basado en el código de salida + if ($exitCode !== 0) { + return new JsonResponse( + ['error' => 'SERVER_ERROR', 'message' => $result['error'] ?? 'Failed to set ogLive as default.'], + Response::HTTP_INTERNAL_SERVER_ERROR + ); } - return new JsonResponse(['message' => 'ogLive client set as default successfully'], Response::HTTP_OK); + // Devolver la respuesta en el formato solicitado + return new JsonResponse( + ['success' => 'ogLive client set as default successfully', 'message' => $result], + Response::HTTP_OK + ); } /** @@ -502,50 +571,60 @@ public function setOgliveDefault(Request $request): Response * ) */ -public function installOglive(Request $request): JsonResponse -{ - // Obtener los datos del cuerpo de la petición - $data = json_decode($request->getContent(), true); - - if (!isset($data['url']) || !isset($data['id'])) { - return new JsonResponse(['error' => 'Invalid input data'], JsonResponse::HTTP_BAD_REQUEST); - } - - // Obtener la URL del ISO y el ID de la transacción - $isoUrl = $data['url']; - $transactionId = $data['id']; - - try { - // Ejecutar el comando Symfony en segundo plano - $consolePath = $this->params->get('kernel.project_dir') . '/bin/console'; - $this->logger->info($consolePath); - $process = new Process([ - 'php', - $consolePath, - 'oglive:install', - $isoUrl, // Sin escapeshellarg - $transactionId // Sin escapeshellarg, - ]); - $commandLine = $process->getCommandLine(); - $this->logger->info('Command to be executed: ' . $commandLine); - #$process->start(); // Ejecuta en segundo plano - $process->setTimeout(null); - $process->disableOutput(); - $process->start(); - - return new JsonResponse([ - 'success' => 'ogLive client installation started', - 'transaction_id' => $transactionId - ], JsonResponse::HTTP_ACCEPTED); - - } catch (Exception $e) { - // Si ocurre algún error durante el proceso de inicio - return new JsonResponse([ - 'error' => 'Failed to initiate ogLive installation', - 'details' => $e->getMessage() - ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); - } -} + public function installOglive(Request $request): JsonResponse + { + // Obtener los datos del cuerpo de la petición + $data = json_decode($request->getContent(), true); + + if (!isset($data['url']) || !isset($data['id'])) { + return new JsonResponse(['error' => 'Invalid input data'], JsonResponse::HTTP_BAD_REQUEST); + } + + // Obtener la URL del ISO y el ID de la transacción + $isoUrl = $data['url']; + $transactionId = $data['id']; + + try { + // Ejecutar el comando Symfony en segundo plano + $consolePath = $this->params->get('kernel.project_dir') . '/bin/console'; + $this->logger->info($consolePath); + $process = new Process([ + $consolePath, + 'oglive:install', + $isoUrl, // Sin escapeshellarg + $transactionId // Sin escapeshellarg, + ]); + $command = sprintf( + 'php %s oglive:install %s %s > /dev/null 2>&1 ', + escapeshellarg($consolePath), + escapeshellarg($isoUrl), + escapeshellarg($transactionId) + ); + $commandLine = $process->getCommandLine(); + $this->logger->info('Command to be executed: ' . $commandLine); + $process = Process::fromShellCommandline($command); + #$process->start(); // Ejecuta en segundo plano + $process->setTimeout(null); + $process->disableOutput(); + try { + $process->start(); + } catch (\Exception $e) { + $this->logger->error('Error starting process: ' . $e->getMessage()); + } + return new JsonResponse([ + 'success' => 'ogLive client installation started', + 'transaction_id' => $transactionId + ], JsonResponse::HTTP_ACCEPTED); + + } catch (Exception $e) { + // Si ocurre algún error durante el proceso de inicio + return new JsonResponse([ + 'error' => 'Failed to initiate ogLive installation', + 'details' => $e->getMessage() + ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR); + } + } + /** * Enviar el resultado al webhook * @@ -619,8 +698,8 @@ public function uninstallOglive(string $checksum): Response $exitCode = $response['exitCode']; // Verificar la respuesta del servicio según el código de salida - error_log("Service call result: " . print_r($result, true)); - error_log("Exit code: " . $exitCode); + $this->logger->info("Service call result: " . print_r($result, true)); + $this->logger->info("Exit code: " . $exitCode); if ($exitCode === 404) { // ogLive client no encontrado @@ -758,6 +837,7 @@ public function getBootFile(string $mac): Response $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; // Genera el nombre del archivo basado en la dirección MAC + $mac = $this->validateAndFormatMac($mac ?? null); $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; @@ -767,185 +847,259 @@ public function getBootFile(string $mac): Response } $content = file_get_contents($filePath); - $templateName = ''; + $templateName = 'desconocido'; // Valor por defecto si no se encuentra la plantilla $contentLines = explode("\n", $content); + + // Buscar la plantilla utilizada foreach ($contentLines as $line) { - if (strpos($line, '#template_name:') !== false) { - $templateName = trim(str_replace('#template_name:', '', $line)); + if (strpos($line, '#Template:') !== false) { + $templateName = trim(str_replace('#Template:', '', $line)); break; } } // Extraer los parámetros del contenido del archivo iPXE preg_match('/set kernelargs (.*)/', $content, $matches); - if (isset($matches[1])) { - $kernelargs = $matches[1]; - parse_str(str_replace(' ', '&', $kernelargs), $params); - - $result = [ + + // Si no encontramos 'kernelargs', podría ser un archivo de arranque por disco + if (!isset($matches[1])) { + return new JsonResponse([ 'template_name' => $templateName, 'mac' => $mac, - 'lang' => $params['LANG'] ?? '', - 'ip' => explode(':', $params['ip'])[0] ?? '', - 'server_ip' => explode(':', $params['ip'])[1] ?? '', - 'router' => explode(':', $params['ip'])[2] ?? '', - 'netmask' => explode(':', $params['ip'])[3] ?? '', - 'computer_name' => explode(':', $params['ip'])[4] ?? '', - 'netiface' => explode(':', $params['ip'])[5] ?? '', - 'group' => $params['group'] ?? '', - 'ogrepo' => $params['ogrepo'] ?? '', - 'oglive' => $params['oglive'] ?? '', - 'oglog' => $params['oglog'] ?? '', - 'ogshare' => $params['ogshare'] ?? '', - 'oglivedir' => $params['oglivedir'] ?? '', - 'ogprof' => $params['ogprof'] ?? '', - 'hardprofile' => $params['hardprofile'] ?? '', - 'ogntp' => $params['ogntp'] ?? '', - 'ogdns' => $params['ogdns'] ?? '', - 'ogproxy' => $params['ogproxy'] ?? '', - 'ogunit' => $params['ogunit'] ?? '', - 'resolution' => $params['vga'] ?? '', - ]; - - return new JsonResponse($result, Response::HTTP_OK); + 'message' => 'Archivo de arranque sin parámetros, posiblemente una plantilla de arranque por disco' + ], Response::HTTP_OK); } - return new JsonResponse(['error' => 'Failed to parse kernel arguments from the boot file'], Response::HTTP_INTERNAL_SERVER_ERROR); + $kernelargs = $matches[1]; + parse_str(str_replace(' ', '&', $kernelargs), $params); + + // Crear la estructura del resultado, incluso si los parámetros son opcionales + $result = [ + 'template_name' => $templateName, + 'mac' => $mac, + 'lang' => $params['LANG'] ?? '', + 'ip' => explode(':', $params['ip'])[0] ?? '', + 'server_ip' => explode(':', $params['ip'])[1] ?? '', + 'router' => explode(':', $params['ip'])[2] ?? '', + 'netmask' => explode(':', $params['ip'])[3] ?? '', + 'computer_name' => explode(':', $params['ip'])[4] ?? '', + 'netiface' => explode(':', $params['ip'])[5] ?? '', + 'group' => $params['group'] ?? '', + 'ogrepo' => $params['ogrepo'] ?? '', + 'oglive' => $params['oglive'] ?? '', + 'oglog' => $params['oglog'] ?? '', + 'ogshare' => $params['ogshare'] ?? '', + 'oglivedir' => $params['oglivedir'] ?? '', + 'ogprof' => $params['ogprof'] ?? '', + 'hardprofile' => $params['hardprofile'] ?? '', + 'ogntp' => $params['ogntp'] ?? '', + 'ogdns' => $params['ogdns'] ?? '', + 'ogproxy' => $params['ogproxy'] ?? '', + 'ogunit' => $params['ogunit'] ?? '', + 'resolution' => $params['vga'] ?? '', + ]; + + return new JsonResponse($result, Response::HTTP_OK); } - /** - * @Route("/ogboot/v1/pxes", name="create_boot_file", methods={"POST"}) - * @OA\Post( - * path="/ogboot/v1/pxes", - * summary="Create a new PXE boot file", - * @OA\RequestBody( - * required=true, - * @OA\JsonContent( - * type="object", - * @OA\Property(property="template_name", type="string", description="Template name", example="pxe"), - * @OA\Property(property="mac", type="string", description="MAC address", example="00:50:56:22:11:12"), - * @OA\Property(property="lang", type="string", description="Language", example="es_ES.UTF-8"), - * @OA\Property(property="ip", type="string", description="IP address", example="192.168.2.11"), - * @OA\Property(property="server_ip", type="string", description="Server IP address", example="192.168.2.1"), - * @OA\Property(property="router", type="string", description="Router", example="192.168.2.1"), - * @OA\Property(property="netmask", type="string", description="Netmask", example="255.255.255.0"), - * @OA\Property(property="computer_name", type="string", description="Computer name", example="pc11"), - * @OA\Property(property="netiface", type="string", description="Network interface", example="eth0"), - * @OA\Property(property="group", type="string", description="Group", example="Aula_virtual"), - * @OA\Property(property="ogrepo", type="string", description="Repository IP", example="192.168.2.1"), - * @OA\Property(property="oglive", type="string", description="Live server IP", example="192.168.2.1"), - * @OA\Property(property="oglog", type="string", description="Log server IP", example="192.168.2.1"), - * @OA\Property(property="ogshare", type="string", description="Share server IP", example="192.168.2.1"), - * @OA\Property(property="oglivedir", type="string", description="Live directory", example="ogLive"), - * @OA\Property(property="ogprof", type="string", description="Is professor", example="false"), - * @OA\Property(property="hardprofile", type="string", description="Hardware profile", example=""), - * @OA\Property(property="ogntp", type="string", description="NTP server", example=""), - * @OA\Property(property="ogdns", type="string", description="DNS server", example=""), - * @OA\Property(property="ogproxy", type="string", description="Proxy server", example=""), - * @OA\Property(property="ogunit", type="string", description="Unit directory", example=""), - * @OA\Property(property="resolution", type="string", description="Screen resolution", example="788") - * ) - * ), - * @OA\Response( - * response=200, - * description="PXE boot file created successfully", - * @OA\JsonContent( - * type="object", - * @OA\Property(property="message", type="string") - * ) - * ), - * @OA\Response( - * response=400, - * description="Invalid input" - * ), - * @OA\Response( - * response=404, - * description="Template not found" - * ), - * @OA\Response( - * response=500, - * description="Internal server error" - * ) - * ) - */ - public function createBootFile(Request $request): JsonResponse + +/** + * @Route("/ogboot/v1/pxes", name="create_boot_file", methods={"POST"}) + * @OA\Post( + * path="/ogboot/v1/pxes", + * summary="Create a new PXE boot file", + * description="Creates a PXE boot file based on the provided template and parameters. All parameters except 'template_name' and 'mac' are optional.", + * @OA\RequestBody( + * required=true, + * @OA\JsonContent( + * type="object", + * @OA\Property(property="template_name", type="string", description="Template name (required)", example="pxe"), + * @OA\Property(property="mac", type="string", description="MAC address (required)", example="00:50:56:22:11:12"), + * @OA\Property(property="lang", type="string", description="Language (optional)", example="es_ES.UTF-8"), + * @OA\Property(property="ip", type="string", description="IP address (optional)", example="192.168.2.11"), + * @OA\Property(property="server_ip", type="string", description="Server IP address (optional)", example="192.168.2.1"), + * @OA\Property(property="router", type="string", description="Router (optional)", example="192.168.2.1"), + * @OA\Property(property="netmask", type="string", description="Netmask (optional)", example="255.255.255.0"), + * @OA\Property(property="computer_name", type="string", description="Computer name (optional)", example="pc11"), + * @OA\Property(property="netiface", type="string", description="Network interface (optional)", example="eth0"), + * @OA\Property(property="group", type="string", description="Group (optional)", example="Aula_virtual"), + * @OA\Property(property="ogrepo", type="string", description="Repository IP (optional)", example="192.168.2.1"), + * @OA\Property(property="oglive", type="string", description="Live server IP (optional)", example="192.168.2.1"), + * @OA\Property(property="oglog", type="string", description="Log server IP (optional)", example="192.168.2.1"), + * @OA\Property(property="ogshare", type="string", description="Share server IP (optional)", example="192.168.2.1"), + * @OA\Property(property="oglivedir", type="string", description="Live directory (optional)", example="ogLive"), + * @OA\Property(property="ogprof", type="string", description="Is professor (optional)", example="false"), + * @OA\Property(property="hardprofile", type="string", description="Hardware profile (optional)", example=""), + * @OA\Property(property="ogntp", type="string", description="NTP server (optional)", example=""), + * @OA\Property(property="ogdns", type="string", description="DNS server (optional)", example=""), + * @OA\Property(property="ogproxy", type="string", description="Proxy server (optional)", example=""), + * @OA\Property(property="ogunit", type="string", description="Unit directory (optional)", example=""), + * @OA\Property(property="resolution", type="string", description="Screen resolution (optional)", example="788") + * ) + * ), + * @OA\Response( + * response=200, + * description="PXE boot file created successfully", + * @OA\JsonContent( + * type="object", + * @OA\Property(property="message", type="string") + * ) + * ), + * @OA\Response( + * response=400, + * description="Invalid input", + * @OA\JsonContent( + * oneOf={ + * @OA\Schema( + * @OA\Property(property="error", type="string", example="Missing required fields"), + * @OA\Property(property="details", type="string", example="MAC and template_name are required") + * ), + * @OA\Schema( + * @OA\Property(property="error", type="string", example="Invalid MAC address"), + * @OA\Property(property="details", type="string", example="MAC address contains invalid characters") + * ), + * @OA\Schema( + * @OA\Property(property="error", type="string", example="Template not found"), + * @OA\Property(property="details", type="string", example="The specified template does not exist") + * ) + * } + * ) + * ), + * @OA\Response( + * response=404, + * description="Template not found", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="Template not found") + * ) + * ), + * @OA\Response( + * response=500, + * description="Internal server error", + * @OA\JsonContent( + * oneOf={ + * @OA\Schema( + * @OA\Property(property="error", type="string", example="Failed to create PXE boot file"), + * @OA\Property(property="details", type="string", example="Error writing to file system") + * ), + * @OA\Schema( + * @OA\Property(property="error", type="string", example="Failed to read template"), + * @OA\Property(property="details", type="string", example="Could not access the template file") + * ) + * } + * ) + * ) + * ) + */ +public function createBootFile(Request $request): JsonResponse +{ + $data = json_decode($request->getContent(), true); + + $templateName = $data['template_name'] ?? null; + $mac = $this->validateAndFormatMac($data['mac'] ?? null); + $serverIp = $data['server_ip'] ?? null; + $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; + + // Verificación de los campos obligatorios + if (!$templateName || !$mac) { + return new JsonResponse(['error' => 'Missing required fields: mac and template_name'], Response::HTTP_BAD_REQUEST); + } + + // Parámetros opcionales + $parameters = [ + 'LANG' => $data['lang'] ?? 'es_ES.UTF-8', + 'ip' => $data['ip'] ?? '', + 'router' => $data['router'] ?? '', + 'netmask' => $data['netmask'] ?? '', + 'computer_name' => $data['computer_name'] ?? '', + 'netiface' => $data['netiface'] ?? '', + 'group' => $data['group'] ?? '', + 'ogrepo' => $data['ogrepo'] ?? '', + 'oglive' => $data['oglive'] ?? $serverIp, + 'oglog' => $data['oglog'] ?? $serverIp, + 'ogshare' => $data['ogshare'] ?? $serverIp, + 'oglivedir' => $data['oglivedir'] ?? '', + 'ogprof' => $data['ogprof'] ?? 'false', + 'hardprofile' => $data['hardprofile'] ?? '', + 'ogntp' => $data['ogntp'] ?? '', + 'ogdns' => $data['ogdns'] ?? '', + 'ogproxy' => $data['ogproxy'] ?? '', + 'ogunit' => $data['ogunit'] ?? '', + 'resolution' => $data['resolution'] ?? '788' + ]; + + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $templatePath = $templateDir . '/' . $templateName; + + // Verificar si la plantilla existe + if (!file_exists($templatePath)) { + return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND); + } + + $templateContent = file_get_contents($templatePath); + if ($templateContent === false) { + return new JsonResponse(['error' => 'Failed to read template'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + // Construcción de los argumentos del kernel + $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . + 'oglivedir=' . $parameters['oglivedir'] . ' ' . + 'LANG=' . $parameters['LANG'] . ' ' . + 'ip=' . $parameters['ip'] . ':' . $serverIp . ':' . $parameters['router'] . ':' . $parameters['netmask'] . ':' . $parameters['computer_name'] . ':' . $parameters['netiface'] . ':none ' . + 'group=' . str_replace(' ', '_', trim($parameters['group'])) . ' ' . + 'ogrepo=' . $parameters['ogrepo'] . ' ' . + 'oglive=' . $serverIp . ' ' . + 'oglog=' . $serverIp . ' ' . + 'ogshare=' . $serverIp . ' ' . + 'ogprof=' . ($parameters['ogprof'] === 'true' ? 'true' : 'false') . ' ' . + (!empty($parameters['hardprofile']) ? 'hardprofile=' . str_replace(' ', '_', trim($parameters['hardprofile'])) . ' ' : '') . + (!empty($parameters['ogntp']) ? 'ogntp=' . $parameters['ogntp'] . ' ' : '') . + (!empty($parameters['ogdns']) ? 'ogdns=' . $parameters['ogdns'] . ' ' : '') . + (!empty($parameters['ogproxy']) ? 'ogproxy=' . $parameters['ogproxy'] . ' ' : '') . + (!empty($parameters['ogunit']) ? 'ogunit=' . $parameters['ogunit'] . ' ' : '') . + (is_numeric($parameters['resolution']) && $parameters['resolution'] <= 999 ? 'vga=' . $parameters['resolution'] : + (strpos($parameters['resolution'], ':') !== false ? 'video=' . $parameters['resolution'] : ' ' . $parameters['resolution'])); + + // Insertar un comentario al principio del archivo con el nombre de la plantilla usada + $pxeContent = "#Template: $templateName\n" . + str_replace(['__INFOHOST__', '__SERVERIP__', '__OGLIVE__'], [$kernelArgs, $serverIp, $ogLiveDir], $templateContent); + + // Nombre del archivo PXE basado en la MAC + $pxeFileName = '01-' . $mac; + + // Ruta para guardar el archivo PXE + $pxeFilePath = '/opt/ogboot/tftpboot/ipxe_scripts/' . $pxeFileName; + + // Crear el archivo PXE + if (file_put_contents($pxeFilePath, $pxeContent) === false) { + return new JsonResponse(['error' => 'Failed to create PXE boot file'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + // Retornar la plantilla creada en el formato solicitado + return new JsonResponse([ + 'success' => 'Plantilla creada con exito', + 'message' => $pxeContent + ], Response::HTTP_OK); +} + + + function validateAndFormatMac($mac) { - $data = json_decode($request->getContent(), true); - - $templateName = $data['template_name'] ?? null; - $mac = $data['mac'] ?? null; - $serverIp = $data['server_ip'] ?? null; - $ogLiveDir = $data['oglivedir'] ?? 'ogLive'; - - if (!$templateName || !$mac || !$serverIp) { - return new JsonResponse(['error' => 'Invalid input'], Response::HTTP_BAD_REQUEST); + // Primero, convertir a mayúsculas para facilitar la comparación + $mac = strtoupper($mac); + + // Comprobar si la MAC tiene el formato correcto con dos puntos (ej. FC:AA:14:21:55:B9) + if (preg_match('/^([0-9A-F]{2}:){5}[0-9A-F]{2}$/', $mac)) { + return $mac; // MAC ya está en el formato correcto } - - $parameters = [ - 'LANG' => $data['lang'] ?? 'es_ES.UTF-8', - 'ip' => $data['ip'] ?? '', - 'router' => $data['router'] ?? '', - 'netmask' => $data['netmask'] ?? '', - 'computer_name' => $data['computer_name'] ?? '', - 'netiface' => $data['netiface'] ?? '', - 'group' => $data['group'] ?? '', - 'ogrepo' => $data['ogrepo'] ?? '', - 'oglive' => $data['oglive'] ?? $serverIp, - 'oglog' => $data['oglog'] ?? $serverIp, - 'ogshare' => $data['ogshare'] ?? $serverIp, - 'oglivedir' => $data['oglivedir'] ?? '', - 'ogprof' => $data['ogprof'] ?? 'false', - 'hardprofile' => $data['hardprofile'] ?? '', - 'ogntp' => $data['ogntp'] ?? '', - 'ogdns' => $data['ogdns'] ?? '', - 'ogproxy' => $data['ogproxy'] ?? '', - 'ogunit' => $data['ogunit'] ?? '', - 'resolution' => $data['resolution'] ?? '788' - ]; - - $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; - $templatePath = $templateDir . '/' . $templateName; - - if (!file_exists($templatePath)) { - return new JsonResponse(['error' => 'Template not found'], Response::HTTP_NOT_FOUND); + + // Comprobar si la MAC tiene el formato sin los dos puntos (ej. FCAA142155B9) + if (preg_match('/^[0-9A-F]{12}$/', $mac)) { + // Insertar los dos puntos en la MAC y devolverla en el formato correcto + return strtoupper(implode(':', str_split($mac, 2))); } - - $templateContent = file_get_contents($templatePath); - if ($templateContent === false) { - return new JsonResponse(['error' => 'Failed to read template'], Response::HTTP_INTERNAL_SERVER_ERROR); - } - - $kernelArgs = 'ro boot=oginit quiet splash irqpoll acpi=on og2nd=sqfs ogprotocol=smb ogactiveadmin=true ogdebug=true ogtmpfs=15 ' . - 'oglivedir=' . $parameters['oglivedir'] . ' ' . - 'LANG=' . $parameters['LANG'] . ' ' . - 'ip=' . $parameters['ip'] . ':' . $serverIp . ':' . $parameters['router'] . ':' . $parameters['netmask'] . ':' . $parameters['computer_name'] . ':' . $parameters['netiface'] . ':none ' . - 'group=' . str_replace(' ', '_', trim($parameters['group'])) . ' ' . - 'ogrepo=' . $parameters['ogrepo'] . ' ' . - 'oglive=' . $serverIp . ' ' . - 'oglog=' . $serverIp . ' ' . - 'ogshare=' . $serverIp . ' ' . - 'ogprof=' . ($parameters['ogprof'] === 'true' ? 'true' : 'false') . ' ' . - (!empty($parameters['hardprofile']) ? 'hardprofile=' . str_replace(' ', '_', trim($parameters['hardprofile'])) . ' ' : '') . - (!empty($parameters['ogntp']) ? 'ogntp=' . $parameters['ogntp'] . ' ' : '') . - (!empty($parameters['ogdns']) ? 'ogdns=' . $parameters['ogdns'] . ' ' : '') . - (!empty($parameters['ogproxy']) ? 'ogproxy=' . $parameters['ogproxy'] . ' ' : '') . - (!empty($parameters['ogunit']) ? 'ogunit=' . $parameters['ogunit'] . ' ' : '') . - (is_numeric($parameters['resolution']) && $parameters['resolution'] <= 999 ? 'vga=' . $parameters['resolution'] : - (strpos($parameters['resolution'], ':') !== false ? 'video=' . $parameters['resolution'] : ' ' . $parameters['resolution'])); - - - $pxeContent = str_replace(['__INFOHOST__', '__SERVERIP__', '__OGLIVE__'], [$kernelArgs, $serverIp, $ogLiveDir], $templateContent); - - - $pxeFileName = '01-' . $mac; - - $pxeFilePath = '/opt/ogboot/tftpboot/ipxe_scripts/' . $pxeFileName; - - if (file_put_contents($pxeFilePath, $pxeContent) === false) { - return new JsonResponse(['error' => 'Failed to create PXE boot file'], Response::HTTP_INTERNAL_SERVER_ERROR); - } - - return new JsonResponse(['message' => 'PXE boot file created successfully'], Response::HTTP_OK); + + // Si no cumple con ninguno de los formatos, devolver un error + return false; // MAC no válida } /** @@ -991,6 +1145,8 @@ public function getBootFile(string $mac): Response { $directory = '/opt/ogboot/tftpboot/ipxe_scripts'; + $mac = $this->validateAndFormatMac($mac ?? null); + $fileName = "01-" . $mac; $filePath = "$directory/$fileName"; @@ -1052,53 +1208,77 @@ public function getBootFile(string $mac): Response return new JsonResponse(['templates' => array_values($templates)], Response::HTTP_OK); } - /** - * @Route("/ogboot/v1/pxe-templates/{templateName}", name="get_template", methods={"GET"}) - * @OA\Get( - * path="/ogboot/v1/pxe-templates/{templateName}", - * summary="Obtener contenido de una plantilla PXE", - * description="Obtiene el contenido de una plantilla de arranque PXE específica por su nombre.", - * @OA\Parameter( - * name="templateName", - * in="path", - * required=true, - * description="Nombre de la plantilla PXE", - * @OA\Schema(type="string", example="mi_plantilla.ipxe") - * ), - * @OA\Response( - * response=200, - * description="Contenido de la plantilla de arranque PXE", - * @OA\MediaType( - * mediaType="text/plain", - * @OA\Schema(type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\n\nset ISODIR ogLive\nset default 0\nset kernelargs INFOHOST\nkernel tftp://SERVERIP/ogLive/ogvmlinuz ${kernelargs}\ninitrd tftp://SERVERIP/ogLive/oginitrd.img\nboot") - * ) - * ), - * @OA\Response( - * response=404, - * description="Plantilla no encontrada" - * ), - * @OA\Response( - * response=500, - * description="Error interno del servidor" - * ) - * ) - */ +/** + * @Route("/ogboot/v1/pxe-templates/{templateName}", name="get_template", methods={"GET"}) + * @OA\Get( + * path="/ogboot/v1/pxe-templates/{templateName}", + * summary="Obtener contenido de una plantilla PXE", + * description="Obtiene el contenido de una plantilla de arranque PXE específica por su nombre.", + * @OA\Parameter( + * name="templateName", + * in="path", + * required=true, + * description="Nombre de la plantilla PXE", + * @OA\Schema(type="string", example="mi_plantilla.ipxe") + * ), + * @OA\Response( + * response=200, + * description="Contenido de la plantilla de arranque PXE", + * @OA\JsonContent( + * @OA\Property(property="success", type="string", example="Plantilla obtenida con éxito"), + * @OA\Property(property="template_name", type="string", example="mi_plantilla.ipxe"), + * @OA\Property(property="template_content", type="string", example="#!ipxe\nset timeout 0\nset timeout-style hidden\n\nset ISODIR ogLive\nset default 0\nset kernelargs INFOHOST\nkernel tftp://SERVERIP/ogLive/ogvmlinuz ${kernelargs}\ninitrd tftp://SERVERIP/ogLive/oginitrd.img\nboot") + * ) + * ), + * @OA\Response( + * response=404, + * description="Plantilla no encontrada", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="TEMPLATE_NOT_FOUND"), + * @OA\Property(property="message", type="string", example="No se encontró la plantilla especificada") + * ) + * ), + * @OA\Response( + * response=500, + * description="Error interno del servidor", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="FAILED_TO_READ_TEMPLATE"), + * @OA\Property(property="message", type="string", example="Error al leer la plantilla") + * ) + * ) + * ) + */ + public function getTemplate(string $templateName): Response { $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; $filePath = "$templateDir/$templateName"; - + + // Comprobar si el archivo de plantilla existe if (!file_exists($filePath)) { - return new Response(null, Response::HTTP_NOT_FOUND); + return new Response(json_encode([ + 'error' => 'TEMPLATE_NOT_FOUND', + 'message' => 'No se encontró la plantilla especificada' + ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); } - + + // Intentar leer el contenido de la plantilla $content = file_get_contents($filePath); if ($content === false) { - return new Response('Error al leer la plantilla', Response::HTTP_INTERNAL_SERVER_ERROR); + return new Response(json_encode([ + 'error' => 'FAILED_TO_READ_TEMPLATE', + 'message' => 'Error al leer la plantilla' + ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); } - - return new Response($content, Response::HTTP_OK, ['Content-Type' => 'text/plain']); + + // Devolver la respuesta con el nombre y contenido de la plantilla + return new Response(json_encode([ + 'success' => 'Plantilla obtenida con éxito', + 'template_name' => $templateName, + 'template_content' => $content + ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); } + /** * @Route("/ogboot/v1/pxe-templates", methods={"POST"}) @@ -1173,68 +1353,90 @@ public function createTemplate(Request $request) return new Response('Ocurrió un error al crear la plantilla de arranque. ' . $e->getMessage(), 500); } + // Retornar el nombre de la plantilla y su contenido en el formato solicitado return new Response(json_encode([ - 'message' => 'Plantilla creada exitosamente.', - 'template' => $contentTemplate + 'success' => 'Plantilla creada con éxito', + 'template_name' => $nameTemplate, + 'template_content' => $contentTemplate ]), 200, ['Content-Type' => 'application/json']); } +/** + * @Route("/ogboot/v1/pxe-templates/{name}", methods={"DELETE"}) + * + * @OA\Delete( + * path="/ogboot/v1/pxe-templates/{name}", + * summary="Eliminar Plantilla", + * description="Elimina una plantilla de arranque específica utilizando su nombre.", + * @OA\Parameter( + * name="name", + * in="path", + * required=true, + * description="El nombre de la plantilla de arranque que se desea eliminar.", + * @OA\Schema(type="string") + * ), + * @OA\Response( + * response=200, + * description="La plantilla de arranque se eliminó correctamente.", + * @OA\JsonContent( + * @OA\Property(property="success", type="string", example="Plantilla eliminada"), + * @OA\Property(property="message", type="string", example="La plantilla mi_plantilla se ha borrado correctamente.") + * ) + * ), + * @OA\Response( + * response=404, + * description="No se encontró ninguna plantilla de arranque con el nombre especificado.", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="TEMPLATE_NOT_FOUND"), + * @OA\Property(property="message", type="string", example="No se encontró la plantilla especificada") + * ) + * ), + * @OA\Response( + * response=500, + * description="Ocurrió un error al eliminar la plantilla de arranque.", + * @OA\JsonContent( + * @OA\Property(property="error", type="string", example="FAILED_TO_DELETE_TEMPLATE"), + * @OA\Property(property="message", type="string", example="Ocurrió un error al intentar eliminar la plantilla") + * ) + * ) + * ) + */ +public function deleteTemplate($name): Response +{ + $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; + $filePath = $templateDir . '/' . $name; - /** - * @Route("/ogboot/v1/pxe-templates/{name}", methods={"DELETE"}) - * - * @OA\Delete( - * path="/ogboot/v1/pxe-templates/{name}", - * summary="Eliminar Plantilla", - * description="Elimina una plantilla de arranque específica utilizando su nombre.", - * @OA\Parameter( - * name="name", - * in="path", - * required=true, - * description="El nombre de la plantilla de arranque que se desea eliminar.", - * @OA\Schema(type="string") - * ), - * @OA\Response( - * response=200, - * description="La plantilla de arranque se eliminó correctamente.", - * @OA\JsonContent( - * type="object", - * @OA\Property(property="message", type="string", example="Plantilla eliminada correctamente.") - * ) - * ), - * @OA\Response( - * response=404, - * description="No se encontró ninguna plantilla de arranque con el nombre especificado." - * ), - * @OA\Response( - * response=500, - * description="Ocurrió un error al eliminar la plantilla de arranque." - * ) - * ) - */ - public function deleteTemplate($name) - { - $templateDir = '/opt/ogboot/tftpboot/ipxe_scripts/templates'; - $filePath = $templateDir . '/' . $name; - - if (!file_exists($filePath)) { - return new Response('No se encontró ninguna plantilla de arranque con el nombre especificado.', 404); - } - - try { - unlink($filePath); - } catch (\Exception $e) { - return new Response('Ocurrió un error al eliminar la plantilla de arranque.', 500); - } - + // Comprobar si la plantilla existe + if (!file_exists($filePath)) { return new Response(json_encode([ - 'message' => 'Plantilla eliminada correctamente.' - ]), 200, ['Content-Type' => 'application/json']); + 'error' => 'TEMPLATE_NOT_FOUND', + 'message' => 'No se encontró la plantilla especificada' + ]), Response::HTTP_NOT_FOUND, ['Content-Type' => 'application/json']); } + // Intentar eliminar la plantilla + try { + unlink($filePath); + } catch (\Exception $e) { + return new Response(json_encode([ + 'error' => 'FAILED_TO_DELETE_TEMPLATE', + 'message' => 'Ocurrió un error al intentar eliminar la plantilla' + ]), Response::HTTP_INTERNAL_SERVER_ERROR, ['Content-Type' => 'application/json']); + } + + // Devolver respuesta exitosa + return new Response(json_encode([ + 'success' => 'Plantilla eliminada', + 'message' => "La plantilla $name se ha borrado correctamente" + ]), Response::HTTP_OK, ['Content-Type' => 'application/json']); +} + + + } +