src/Controller/Web/LegApiProxyController.php line 78

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Web;
  3. use App\Entity\LegApiRetryRequest;
  4. use App\Service\LegApiService;
  5. use Doctrine\ORM\EntityManagerInterface;
  6. use Psr\Log\LoggerInterface;
  7. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  8. use Symfony\Component\HttpFoundation\JsonResponse;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\HttpFoundation\Response;
  11. use Symfony\Component\Routing\Annotation\Route;
  12. use Symfony\Contracts\HttpClient\HttpClientInterface;
  13. class LegApiProxyController extends AbstractController
  14. {
  15.     private $logger;
  16.     private $legApiService;
  17.     private $em;
  18.     private $client;
  19.     public function __construct(
  20.         LoggerInterface $logger,
  21.         LegApiService $legApiService,
  22.         EntityManagerInterface $em,
  23.         HttpClientInterface $client
  24.     ) {
  25.         $this->logger $logger;
  26.         $this->legApiService $legApiService;
  27.         $this->em $em;
  28.         $this->client $client;
  29.     }
  30.     /**
  31.      * Liefert CSRF-Token und Cookie von der SAP-API zurück.
  32.      * Wird von Server 1 aufgerufen, da dieser keine direkte VPN-Verbindung hat.
  33.      *
  34.      * @Route("/legapi", name="legapi_getToken")
  35.      */
  36.     public function getTokenAction(Request $request): Response
  37.     {
  38.         $token $this->legApiService->getToken();
  39.         $response = new JsonResponse($token200);
  40.         $response->headers->set('x-csrf-token'$token['x-csrf-token']);
  41.         $response->headers->set('set-cookie'$token['cookie']);
  42.         return $response;
  43.     }
  44.     /**
  45.      * Leitet LIFT_VIEW-GET-Anfragen von Server 1 an die SAP-API weiter.
  46.      *
  47.      * @Route("/legapi/ZLCS_ACOBE_LIFT_VIEW/", name="legapi_liftView")
  48.      */
  49.     public function liftViewAction(Request $request): Response
  50.     {
  51.         $filter $request->get('$filter');
  52.         $tpArray explode("'"$filter ?? '');
  53.         $tp $tpArray[1] ?? 'N/A';
  54.         $this->legApiService->getToken();
  55.         $liftXml $this->legApiService->getLift($tp);
  56.         $response = new Response($liftXml);
  57.         $response->headers->set('Content-Type''application/xml');
  58.         return $response;
  59.     }
  60.     /**
  61.      * Empfängt fertig aufgebauten XML-Body von Server 1 und sendet ihn via VPN an SAP.
  62.      * Protokolliert alle Fehler. Bei HTTP 409 wird der Request für einen Retry in 8h gespeichert.
  63.      *
  64.      * @Route("/legapi/Lift_statusSet", name="legapi_postState", methods={"POST"})
  65.      */
  66.     public function postStateLiftAction(Request $request): Response
  67.     {
  68.         $xml $request->getContent();
  69.         if (empty(trim($xml))) {
  70.             $this->logger->error('LegApiProxy: postStateLift: Leerer XML-Body empfangen');
  71.             return new JsonResponse(['error' => 'Leerer XML-Body'], 400);
  72.         }
  73.         $this->logger->info('LegApiProxy: postStateLift: XML-Body empfangen, Länge=' strlen($xml));
  74.         $result $this->legApiService->postStateXml($xml);
  75.         if ($result['success']) {
  76.             $this->logger->info('LegApiProxy: postStateLift: Erfolgreich gesendet, statusCode=' $result['statusCode']);
  77.             return new Response(''$result['statusCode']);
  78.         }
  79.         $statusCode $result['statusCode'];
  80.         if (409 === $statusCode) {
  81.             $this->logger->warning('LegApiProxy: postStateLift: 409 Conflict – speichere für Retry in 8h');
  82.             $this->save409RetryRequest($xml$statusCode);
  83.             return new Response($result['content'], 409);
  84.         }
  85.         $this->logger->error(
  86.             'LegApiProxy: postStateLift: Fehler statusCode=' $statusCode .
  87.             ' error=' . ($result['error'] ?? $result['content'])
  88.         );
  89.         return new Response($result['content'] ?? ''$statusCode ?: 502);
  90.     }
  91.     /**
  92.      * 1:1-Proxy für Anfragen die über VPN von SAP eingehen und an Server 1 weitergeleitet werden sollen.
  93.      * Die Original-Headers (inkl. Authorization) werden durchgereicht.
  94.      *
  95.      * @Route("/legapi-forward/{path}", name="legapi_forward_to_server1", requirements={"path"=".+"})
  96.      */
  97.     public function forwardToServer1Action(string $pathRequest $request): Response
  98.     {
  99.         $server1Url $this->legApiService->getServer1Url();
  100.         if (empty($server1Url)) {
  101.             $this->logger->error('LegApiProxy: forwardToServer1: Server1-URL nicht konfiguriert (SERVERNAME=' . ($_ENV['SERVERNAME'] ?? 'unbekannt') . ')');
  102.             return new JsonResponse(['error' => 'Proxy nicht konfiguriert – nur auf vpnmanager/vpnstaging verfügbar'], 503);
  103.         }
  104.         $targetUrl rtrim($server1Url'/') . '/' ltrim($path'/');
  105.         $queryString $request->getQueryString();
  106.         if ($queryString) {
  107.             $targetUrl .= '?' $queryString;
  108.         }
  109.         $this->logger->info('LegApiProxy: forwardToServer1: ' $request->getMethod() . ' ' $targetUrl);
  110.         $headers = [];
  111.         foreach ($request->headers->all() as $name => $values) {
  112.             if (in_array(strtolower($name), ['host''content-length''transfer-encoding'])) {
  113.                 continue;
  114.             }
  115.             $headers[$name] = implode(', '$values);
  116.         }
  117.         $options = [
  118.             'headers' => $headers,
  119.             'timeout' => 30,
  120.         ];
  121.         $body $request->getContent();
  122.         if (!empty($body)) {
  123.             $options['body'] = $body;
  124.         }
  125.         try {
  126.             $response $this->client->request($request->getMethod(), $targetUrl$options);
  127.             $statusCode $response->getStatusCode();
  128.             $content $response->getContent(false);
  129.             $responseHeaders $response->getHeaders(false);
  130.             $symfonyResponse = new Response($content$statusCode);
  131.             $skipHeaders = ['transfer-encoding''content-encoding'];
  132.             foreach ($responseHeaders as $name => $values) {
  133.                 if (!in_array(strtolower($name), $skipHeaders)) {
  134.                     $symfonyResponse->headers->set($nameimplode(', '$values));
  135.                 }
  136.             }
  137.             return $symfonyResponse;
  138.         } catch (\Exception $e) {
  139.             $this->logger->error('LegApiProxy: forwardToServer1: Exception: ' $e->getMessage());
  140.             return new JsonResponse(['error' => 'Fehler beim Weiterleiten'], 502);
  141.         }
  142.     }
  143.     /**
  144.      * Speichert einen fehlgeschlagenen 409-Request für den Retry nach 8h.
  145.      */
  146.     private function save409RetryRequest(string $xmlint $statusCode): void
  147.     {
  148.         $parsed $this->legApiService->parsePostStateXml($xml);
  149.         $retryRequest = new LegApiRetryRequest();
  150.         $retryRequest->setRequestXml($xml);
  151.         $retryRequest->setLastStatusCode($statusCode);
  152.         $retryRequest->setTechnicalPlace($parsed['technicalPlace'] ?? 'unknown');
  153.         $retryRequest->setSentState($parsed['sentState'] ?? 0);
  154.         $this->em->persist($retryRequest);
  155.         $this->em->flush();
  156.         $this->logger->info(
  157.             'LegApiProxy: 409-Retry gespeichert für TechnicalPlace=' .
  158.             ($parsed['technicalPlace'] ?? 'unknown') .
  159.             ' sentState=' . ($parsed['sentState'] ?? 0)
  160.         );
  161.     }
  162. }