vendor/shopware/core/Framework/Api/Controller/InfoController.php line 114

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Api\Controller;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Content\Flow\Api\FlowActionCollector;
  5. use Shopware\Core\Framework\Api\ApiDefinition\DefinitionService;
  6. use Shopware\Core\Framework\Api\ApiDefinition\Generator\EntitySchemaGenerator;
  7. use Shopware\Core\Framework\Api\ApiDefinition\Generator\OpenApi3Generator;
  8. use Shopware\Core\Framework\Bundle;
  9. use Shopware\Core\Framework\Context;
  10. use Shopware\Core\Framework\Event\BusinessEventCollector;
  11. use Shopware\Core\Framework\Feature;
  12. use Shopware\Core\Framework\Increment\Exception\IncrementGatewayNotFoundException;
  13. use Shopware\Core\Framework\Increment\IncrementGatewayRegistry;
  14. use Shopware\Core\Framework\Plugin;
  15. use Shopware\Core\Framework\Routing\Annotation\Since;
  16. use Shopware\Core\Framework\Routing\Exception\InvalidRequestParameterException;
  17. use Shopware\Core\Kernel;
  18. use Shopware\Core\Maintenance\System\Service\AppUrlVerifier;
  19. use Shopware\Core\PlatformRequest;
  20. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  21. use Symfony\Component\Asset\PackageInterface;
  22. use Symfony\Component\Asset\Packages;
  23. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  24. use Symfony\Component\HttpFoundation\JsonResponse;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Symfony\Component\Routing\Annotation\Route;
  28. /**
  29.  * @Route(defaults={"_routeScope"={"api"}})
  30.  */
  31. class InfoController extends AbstractController
  32. {
  33.     private DefinitionService $definitionService;
  34.     private ParameterBagInterface $params;
  35.     private Packages $packages;
  36.     private Kernel $kernel;
  37.     private bool $enableUrlFeature;
  38.     /**
  39.      * @var array{administration?: string}
  40.      */
  41.     private array $cspTemplates;
  42.     private BusinessEventCollector $eventCollector;
  43.     private ?FlowActionCollector $flowActionCollector;
  44.     private IncrementGatewayRegistry $incrementGatewayRegistry;
  45.     private Connection $connection;
  46.     private AppUrlVerifier $appUrlVerifier;
  47.     /**
  48.      * @param array{administration?: string} $cspTemplates
  49.      *
  50.      * @internal
  51.      */
  52.     public function __construct(
  53.         DefinitionService $definitionService,
  54.         ParameterBagInterface $params,
  55.         Kernel $kernel,
  56.         Packages $packages,
  57.         BusinessEventCollector $eventCollector,
  58.         IncrementGatewayRegistry $incrementGatewayRegistry,
  59.         Connection $connection,
  60.         AppUrlVerifier $appUrlVerifier,
  61.         ?FlowActionCollector $flowActionCollector null,
  62.         bool $enableUrlFeature true,
  63.         array $cspTemplates = []
  64.     ) {
  65.         $this->definitionService $definitionService;
  66.         $this->params $params;
  67.         $this->packages $packages;
  68.         $this->kernel $kernel;
  69.         $this->enableUrlFeature $enableUrlFeature;
  70.         $this->flowActionCollector $flowActionCollector;
  71.         $this->cspTemplates $cspTemplates;
  72.         $this->eventCollector $eventCollector;
  73.         $this->incrementGatewayRegistry $incrementGatewayRegistry;
  74.         $this->connection $connection;
  75.         $this->appUrlVerifier $appUrlVerifier;
  76.     }
  77.     /**
  78.      * @Since("6.0.0.0")
  79.      * @Route("/api/_info/openapi3.json", defaults={"auth_required"="%shopware.api.api_browser.auth_required_str%"}, name="api.info.openapi3", methods={"GET"})
  80.      */
  81.     public function info(Request $request): JsonResponse
  82.     {
  83.         $apiType $request->query->getAlpha('type'DefinitionService::TypeJsonApi);
  84.         $apiType $this->definitionService->toApiType($apiType);
  85.         if ($apiType === null) {
  86.             throw new InvalidRequestParameterException('type');
  87.         }
  88.         $data $this->definitionService->generate(OpenApi3Generator::FORMATDefinitionService::API$apiType);
  89.         return new JsonResponse($data);
  90.     }
  91.     /**
  92.      * @Since("6.4.6.0")
  93.      * @Route("/api/_info/queue.json", name="api.info.queue", methods={"GET"})
  94.      */
  95.     public function queue(): JsonResponse
  96.     {
  97.         try {
  98.             $gateway $this->incrementGatewayRegistry->get(IncrementGatewayRegistry::MESSAGE_QUEUE_POOL);
  99.         } catch (IncrementGatewayNotFoundException $exception) {
  100.             // In case message_queue pool is disabled
  101.             return new JsonResponse([]);
  102.         }
  103.         // Fetch unlimited message_queue_stats
  104.         $entries $gateway->list('message_queue_stats', -1);
  105.         return new JsonResponse(array_map(function (array $entry) {
  106.             return [
  107.                 'name' => $entry['key'],
  108.                 'size' => (int) $entry['count'],
  109.             ];
  110.         }, array_values($entries)));
  111.     }
  112.     /**
  113.      * @Since("6.0.0.0")
  114.      * @Route("/api/_info/open-api-schema.json", defaults={"auth_required"="%shopware.api.api_browser.auth_required_str%"}, name="api.info.open-api-schema", methods={"GET"})
  115.      */
  116.     public function openApiSchema(): JsonResponse
  117.     {
  118.         $data $this->definitionService->getSchema(OpenApi3Generator::FORMATDefinitionService::API);
  119.         return new JsonResponse($data);
  120.     }
  121.     /**
  122.      * @Since("6.0.0.0")
  123.      * @Route("/api/_info/entity-schema.json", name="api.info.entity-schema", methods={"GET"})
  124.      */
  125.     public function entitySchema(): JsonResponse
  126.     {
  127.         $data $this->definitionService->getSchema(EntitySchemaGenerator::FORMATDefinitionService::API);
  128.         return new JsonResponse($data);
  129.     }
  130.     /**
  131.      * @Since("6.3.2.0")
  132.      * @Route("/api/_info/events.json", name="api.info.business-events", methods={"GET"})
  133.      */
  134.     public function businessEvents(Context $context): JsonResponse
  135.     {
  136.         $events $this->eventCollector->collect($context);
  137.         return new JsonResponse($events);
  138.     }
  139.     /**
  140.      * @Since("6.0.0.0")
  141.      * @Route("/api/_info/swagger.html", defaults={"auth_required"="%shopware.api.api_browser.auth_required_str%"}, name="api.info.swagger", methods={"GET"})
  142.      */
  143.     public function infoHtml(Request $request): Response
  144.     {
  145.         $nonce $request->attributes->get(PlatformRequest::ATTRIBUTE_CSP_NONCE);
  146.         $apiType $request->query->getAlpha('type'DefinitionService::TypeJson);
  147.         $response $this->render(
  148.             '@Framework/swagger.html.twig',
  149.             [
  150.                 'schemaUrl' => 'api.info.openapi3',
  151.                 'cspNonce' => $nonce,
  152.                 'apiType' => $apiType,
  153.             ]
  154.         );
  155.         $cspTemplate $this->cspTemplates['administration'] ?? '';
  156.         $cspTemplate trim($cspTemplate);
  157.         if ($cspTemplate !== '') {
  158.             $csp str_replace('%nonce%'$nonce$cspTemplate);
  159.             $csp str_replace(["\n""\r"], ' '$csp);
  160.             $response->headers->set('Content-Security-Policy'$csp);
  161.         }
  162.         return $response;
  163.     }
  164.     /**
  165.      * @Since("6.0.0.0")
  166.      * @Route("/api/_info/config", name="api.info.config", methods={"GET"})
  167.      *
  168.      * @deprecated tag:v6.5.0 $context param will be required
  169.      * @deprecated tag:v6.5.0 $request param will be required
  170.      */
  171.     public function config(?Context $context null, ?Request $request null): JsonResponse
  172.     {
  173.         if (!$context) {
  174.             Feature::triggerDeprecationOrThrow(
  175.                 'v6.5.0.0',
  176.                 'First parameter `$context` will be required in method `config()` in `InfoController` in v6.5.0.0'
  177.             );
  178.             $context Context::createDefaultContext();
  179.         }
  180.         $appUrlReachable true;
  181.         if ($request) {
  182.             $appUrlReachable $this->appUrlVerifier->isAppUrlReachable($request);
  183.         }
  184.         return new JsonResponse([
  185.             'version' => $this->params->get('kernel.shopware_version'),
  186.             'versionRevision' => $this->params->get('kernel.shopware_version_revision'),
  187.             'adminWorker' => [
  188.                 'enableAdminWorker' => $this->params->get('shopware.admin_worker.enable_admin_worker'),
  189.                 'transports' => $this->params->get('shopware.admin_worker.transports'),
  190.             ],
  191.             'bundles' => $this->getBundles($context),
  192.             'settings' => [
  193.                 'enableUrlFeature' => $this->enableUrlFeature,
  194.                 'appUrlReachable' => $appUrlReachable,
  195.                 'appsRequireAppUrl' => $this->appUrlVerifier->hasAppsThatNeedAppUrl($context),
  196.             ],
  197.         ]);
  198.     }
  199.     /**
  200.      * @Since("6.3.5.0")
  201.      * @Route("/api/_info/version", name="api.info.shopware.version", methods={"GET"})
  202.      * @Route("/api/v1/_info/version", name="api.info.shopware.version_old_version", methods={"GET"})
  203.      */
  204.     public function infoShopwareVersion(): JsonResponse
  205.     {
  206.         return new JsonResponse([
  207.             'version' => $this->params->get('kernel.shopware_version'),
  208.         ]);
  209.     }
  210.     /**
  211.      * @Since("6.4.5.0")
  212.      * @Route("/api/_info/flow-actions.json", name="api.info.actions", methods={"GET"})
  213.      */
  214.     public function flowActions(Context $context): JsonResponse
  215.     {
  216.         if (!$this->flowActionCollector) {
  217.             return $this->json([]);
  218.         }
  219.         $events $this->flowActionCollector->collect($context);
  220.         return new JsonResponse($events);
  221.     }
  222.     /**
  223.      * @return array<string, array{type: 'plugin', css: string[], js: string[], baseUrl: ?string }|array{type: 'app', name: string, active: bool, integrationId: string, baseUrl: string, version: string, permissions: array<string,string[]>}>
  224.      */
  225.     private function getBundles(Context $context): array
  226.     {
  227.         $assets = [];
  228.         $package $this->packages->getPackage('asset');
  229.         foreach ($this->kernel->getBundles() as $bundle) {
  230.             if (!$bundle instanceof Bundle) {
  231.                 continue;
  232.             }
  233.             $bundleDirectoryName preg_replace('/bundle$/'''mb_strtolower($bundle->getName()));
  234.             if ($bundleDirectoryName === null) {
  235.                 throw new \RuntimeException(sprintf('Unable to generate bundle directory for bundle "%s"'$bundle->getName()));
  236.             }
  237.             $styles array_map(static function (string $filename) use ($package$bundleDirectoryName) {
  238.                 $url 'bundles/' $bundleDirectoryName '/' $filename;
  239.                 return $package->getUrl($url);
  240.             }, $this->getAdministrationStyles($bundle));
  241.             $scripts array_map(static function (string $filename) use ($package$bundleDirectoryName) {
  242.                 $url 'bundles/' $bundleDirectoryName '/' $filename;
  243.                 return $package->getUrl($url);
  244.             }, $this->getAdministrationScripts($bundle));
  245.             $baseUrl $this->getBaseUrl($bundle$package$bundleDirectoryName);
  246.             if (empty($styles) && empty($scripts)) {
  247.                 if ($baseUrl === null) {
  248.                     continue;
  249.                 }
  250.             }
  251.             $assets[$bundle->getName()] = [
  252.                 'css' => $styles,
  253.                 'js' => $scripts,
  254.                 'baseUrl' => $baseUrl,
  255.                 'type' => 'plugin',
  256.             ];
  257.         }
  258.         foreach ($this->getActiveApps() as $app) {
  259.             $assets[$app['name']] = [
  260.                 'active' => (bool) $app['active'],
  261.                 'integrationId' => $app['integrationId'],
  262.                 'type' => 'app',
  263.                 'baseUrl' => $app['baseUrl'],
  264.                 'permissions' => $app['privileges'],
  265.                 'version' => $app['version'],
  266.                 'name' => $app['name'],
  267.             ];
  268.         }
  269.         return $assets;
  270.     }
  271.     /**
  272.      * @return list<string>
  273.      */
  274.     private function getAdministrationStyles(Bundle $bundle): array
  275.     {
  276.         $path 'administration/css/' str_replace('_''-'$bundle->getContainerPrefix()) . '.css';
  277.         $bundlePath $bundle->getPath();
  278.         if (!file_exists($bundlePath '/Resources/public/' $path)) {
  279.             return [];
  280.         }
  281.         return [$path];
  282.     }
  283.     /**
  284.      * @return list<string>
  285.      */
  286.     private function getAdministrationScripts(Bundle $bundle): array
  287.     {
  288.         $path 'administration/js/' str_replace('_''-'$bundle->getContainerPrefix()) . '.js';
  289.         $bundlePath $bundle->getPath();
  290.         if (!file_exists($bundlePath '/Resources/public/' $path)) {
  291.             return [];
  292.         }
  293.         return [$path];
  294.     }
  295.     private function getBaseUrl(Bundle $bundlePackageInterface $packagestring $bundleDirectoryName): ?string
  296.     {
  297.         if (!$bundle instanceof Plugin) {
  298.             return null;
  299.         }
  300.         if ($bundle->getAdminBaseUrl()) {
  301.             return $bundle->getAdminBaseUrl();
  302.         }
  303.         $defaultEntryFile 'administration/index.html';
  304.         $bundlePath $bundle->getPath();
  305.         if (!file_exists($bundlePath '/Resources/public/' $defaultEntryFile)) {
  306.             return null;
  307.         }
  308.         $url 'bundles/' $bundleDirectoryName '/' $defaultEntryFile;
  309.         return $package->getUrl($url);
  310.     }
  311.     /**
  312.      * @return list<array{name: string, active: int, integrationId: string, baseUrl: string, version: string, privileges: array<string,list<string>>}>
  313.      */
  314.     private function getActiveApps(): array
  315.     {
  316.         /** @var list<array{name: string, active: int, integrationId: string, baseUrl: string, version: string, privileges: ?string}> $apps */
  317.         $apps $this->connection->fetchAllAssociative('SELECT
  318.     app.name,
  319.     app.active,
  320.     LOWER(HEX(app.integration_id)) as integrationId,
  321.     app.base_app_url as baseUrl,
  322.     app.version,
  323.     ar.privileges as privileges
  324. FROM app
  325. LEFT JOIN acl_role ar on app.acl_role_id = ar.id
  326. WHERE app.active = 1 AND app.base_app_url is not null');
  327.         return array_map(static function (array $item) {
  328.             $privileges $item['privileges'] ? json_decode($item['privileges'], true512\JSON_THROW_ON_ERROR) : [];
  329.             $item['privileges'] = [];
  330.             foreach ($privileges as $privilege) {
  331.                 if (substr_count($privilege':') !== 1) {
  332.                     $item['privileges']['additional'][] = $privilege;
  333.                     continue;
  334.                 }
  335.                 [$entity$key] = \explode(':'$privilege);
  336.                 $item['privileges'][$key][] = $entity;
  337.             }
  338.             return $item;
  339.         }, $apps);
  340.     }
  341. }