<?php
namespace App\Controller\Web;
use App\Entity\LegApiRetryRequest;
use App\Service\LegApiService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class LegApiProxyController extends AbstractController
{
private $logger;
private $legApiService;
private $em;
private $client;
public function __construct(
LoggerInterface $logger,
LegApiService $legApiService,
EntityManagerInterface $em,
HttpClientInterface $client
) {
$this->logger = $logger;
$this->legApiService = $legApiService;
$this->em = $em;
$this->client = $client;
}
/**
* Liefert CSRF-Token und Cookie von der SAP-API zurück.
* Wird von Server 1 aufgerufen, da dieser keine direkte VPN-Verbindung hat.
*
* @Route("/legapi", name="legapi_getToken")
*/
public function getTokenAction(Request $request): Response
{
$token = $this->legApiService->getToken();
$response = new JsonResponse($token, 200);
$response->headers->set('x-csrf-token', $token['x-csrf-token']);
$response->headers->set('set-cookie', $token['cookie']);
return $response;
}
/**
* Leitet LIFT_VIEW-GET-Anfragen von Server 1 an die SAP-API weiter.
*
* @Route("/legapi/ZLCS_ACOBE_LIFT_VIEW/", name="legapi_liftView")
*/
public function liftViewAction(Request $request): Response
{
$filter = $request->get('$filter');
$tpArray = explode("'", $filter ?? '');
$tp = $tpArray[1] ?? 'N/A';
$this->legApiService->getToken();
$liftXml = $this->legApiService->getLift($tp);
$response = new Response($liftXml);
$response->headers->set('Content-Type', 'application/xml');
return $response;
}
/**
* Empfängt fertig aufgebauten XML-Body von Server 1 und sendet ihn via VPN an SAP.
* Protokolliert alle Fehler. Bei HTTP 409 wird der Request für einen Retry in 8h gespeichert.
*
* @Route("/legapi/Lift_statusSet", name="legapi_postState", methods={"POST"})
*/
public function postStateLiftAction(Request $request): Response
{
$xml = $request->getContent();
if (empty(trim($xml))) {
$this->logger->error('LegApiProxy: postStateLift: Leerer XML-Body empfangen');
return new JsonResponse(['error' => 'Leerer XML-Body'], 400);
}
$this->logger->info('LegApiProxy: postStateLift: XML-Body empfangen, Länge=' . strlen($xml));
$result = $this->legApiService->postStateXml($xml);
if ($result['success']) {
$this->logger->info('LegApiProxy: postStateLift: Erfolgreich gesendet, statusCode=' . $result['statusCode']);
return new Response('', $result['statusCode']);
}
$statusCode = $result['statusCode'];
if (409 === $statusCode) {
$this->logger->warning('LegApiProxy: postStateLift: 409 Conflict – speichere für Retry in 8h');
$this->save409RetryRequest($xml, $statusCode);
return new Response($result['content'], 409);
}
$this->logger->error(
'LegApiProxy: postStateLift: Fehler statusCode=' . $statusCode .
' error=' . ($result['error'] ?? $result['content'])
);
return new Response($result['content'] ?? '', $statusCode ?: 502);
}
/**
* 1:1-Proxy für Anfragen die über VPN von SAP eingehen und an Server 1 weitergeleitet werden sollen.
* Die Original-Headers (inkl. Authorization) werden durchgereicht.
*
* @Route("/legapi-forward/{path}", name="legapi_forward_to_server1", requirements={"path"=".+"})
*/
public function forwardToServer1Action(string $path, Request $request): Response
{
$server1Url = $this->legApiService->getServer1Url();
if (empty($server1Url)) {
$this->logger->error('LegApiProxy: forwardToServer1: Server1-URL nicht konfiguriert (SERVERNAME=' . ($_ENV['SERVERNAME'] ?? 'unbekannt') . ')');
return new JsonResponse(['error' => 'Proxy nicht konfiguriert – nur auf vpnmanager/vpnstaging verfügbar'], 503);
}
$targetUrl = rtrim($server1Url, '/') . '/' . ltrim($path, '/');
$queryString = $request->getQueryString();
if ($queryString) {
$targetUrl .= '?' . $queryString;
}
$this->logger->info('LegApiProxy: forwardToServer1: ' . $request->getMethod() . ' ' . $targetUrl);
$headers = [];
foreach ($request->headers->all() as $name => $values) {
if (in_array(strtolower($name), ['host', 'content-length', 'transfer-encoding'])) {
continue;
}
$headers[$name] = implode(', ', $values);
}
$options = [
'headers' => $headers,
'timeout' => 30,
];
$body = $request->getContent();
if (!empty($body)) {
$options['body'] = $body;
}
try {
$response = $this->client->request($request->getMethod(), $targetUrl, $options);
$statusCode = $response->getStatusCode();
$content = $response->getContent(false);
$responseHeaders = $response->getHeaders(false);
$symfonyResponse = new Response($content, $statusCode);
$skipHeaders = ['transfer-encoding', 'content-encoding'];
foreach ($responseHeaders as $name => $values) {
if (!in_array(strtolower($name), $skipHeaders)) {
$symfonyResponse->headers->set($name, implode(', ', $values));
}
}
return $symfonyResponse;
} catch (\Exception $e) {
$this->logger->error('LegApiProxy: forwardToServer1: Exception: ' . $e->getMessage());
return new JsonResponse(['error' => 'Fehler beim Weiterleiten'], 502);
}
}
/**
* Speichert einen fehlgeschlagenen 409-Request für den Retry nach 8h.
*/
private function save409RetryRequest(string $xml, int $statusCode): void
{
$parsed = $this->legApiService->parsePostStateXml($xml);
$retryRequest = new LegApiRetryRequest();
$retryRequest->setRequestXml($xml);
$retryRequest->setLastStatusCode($statusCode);
$retryRequest->setTechnicalPlace($parsed['technicalPlace'] ?? 'unknown');
$retryRequest->setSentState($parsed['sentState'] ?? 0);
$this->em->persist($retryRequest);
$this->em->flush();
$this->logger->info(
'LegApiProxy: 409-Retry gespeichert für TechnicalPlace=' .
($parsed['technicalPlace'] ?? 'unknown') .
' sentState=' . ($parsed['sentState'] ?? 0)
);
}
}