DigiComp.FlowWebManifestUtils/Classes/Http/WebManifestMiddleware.php
Ferdinand Kuhl 6908aad70c
All checks were successful
ci/woodpecker/push/code-style Pipeline was successful
first working version
2024-06-21 13:17:27 +02:00

151 lines
5.8 KiB
PHP

<?php
declare(strict_types=1);
namespace DigiComp\FlowWebManifestUtils\Http;
use GuzzleHttp\Psr7\Response;
use Neos\Cache\Exception as CacheException;
use Neos\Cache\Exception\InvalidDataException;
use Neos\Cache\Frontend\StringFrontend;
use Neos\Flow\Http\Exception;
use Neos\Flow\I18n\Service as I18nService;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Routing\Exception\MissingActionNameException;
use Neos\Flow\Mvc\Routing\UriBuilder;
use Neos\Flow\ResourceManagement\ResourceManager;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class WebManifestMiddleware implements MiddlewareInterface
{
protected int $browserCacheMaxAge;
protected string $defaultName = '';
protected I18nService $i18nService;
protected array $manifestData;
protected string $reactOnPath;
protected ResourceManager $resourceManager;
protected StringFrontend $responseCache;
protected array $uriProperties = ['start_url', 'scope', 'id'];
public function __construct(
int $browserCacheMaxAge,
string $defaultName,
I18nService $i18nService,
array $manifestData,
string $reactOnPath,
ResourceManager $resourceManager,
StringFrontend $responseCache
) {
$this->browserCacheMaxAge = $browserCacheMaxAge;
$this->defaultName = $defaultName;
$this->i18nService = $i18nService;
$this->manifestData = $manifestData;
$this->reactOnPath = $reactOnPath;
$this->resourceManager = $resourceManager;
$this->responseCache = $responseCache;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getUri()->getPath() === $this->reactOnPath) {
return $this->createResponse($request);
}
return $handler->handle($request);
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @throws CacheException
* @throws InvalidDataException
* @throws \JsonException
* @throws Exception
* @throws MissingActionNameException
*/
protected function createResponse(ServerRequestInterface $request): ResponseInterface
{
$cacheId = (string)$this->i18nService->getConfiguration()->getCurrentLocale();
if ($this->responseCache->has($cacheId)) {
$response = $this->responseCache->get($cacheId);
} else {
$result = $this->manifestData;
if ($result['name'] === null) {
$result['name'] = $this->defaultName;
}
if ($result['short_name'] === null) {
$result['short_name'] = $this->defaultName;
}
if ($result['lang'] === null) {
$result['lang'] = \str_replace(
'_',
'-',
(string)$this->i18nService->getConfiguration()->getCurrentLocale()
);
}
foreach ($this->uriProperties as $uriProperty) {
if (\is_array($result[$uriProperty])) {
$uriBuilder = new UriBuilder();
$uriBuilder->setRequest(ActionRequest::fromHttpRequest($request));
if (isset($result[$uriProperty]['format']) && \is_string($result[$uriProperty]['format'])) {
$uriBuilder->setFormat($result[$uriProperty]['format']);
}
if (!isset($result[$uriProperty]['action'])) {
throw new \InvalidArgumentException(
'if ' . $uriProperty . ' is in array form, action is required',
1718957544
);
}
$result[$uriProperty] = $uriBuilder->uriFor(
$result[$uriProperty]['action'],
$result[$uriProperty]['arguments'] ?? [],
$result[$uriProperty]['controller'] ?? null,
$result[$uriProperty]['package'] ?? null,
$result[$uriProperty]['subPackage'] ?? null,
);
}
}
foreach ($result['icons'] as &$iconData) {
if (!isset($iconData['src']) || !isset($iconData['sizes']) || !isset($iconData['type'])) {
throw new \InvalidArgumentException(
'Icons in manifests require src, sizes and type',
1718961281
);
}
if (\str_starts_with($iconData['src'], 'resource://')) {
$iconData['src'] = $this->resourceManager->getPublicPackageResourceUriByPath($iconData['src']);
}
}
unset($iconData);
if ($result['id'] === null) {
$result['id'] = $result['start_url'];
}
$response = \json_encode($result, \JSON_THROW_ON_ERROR);
$this->responseCache->set($cacheId, $response);
}
return new Response(
200,
[
'Content-Type' => 'application/manifest+json',
'Cache-Control' => 'max-age=' . $this->browserCacheMaxAge . ', must-revalidate',
'Last-Modified' => $this->getCacheDate(),
'Vary' => 'Accept, Accept-Language'
],
$response
);
}
protected function getCacheDate(): string
{
if (false === $lastModified = $this->responseCache->get('lastModified')) {
$lastModified = (new \DateTimeImmutable())->format(\DATE_RFC7231);
$this->responseCache->set('lastModified', $lastModified);
}
return $lastModified;
}
}