Ferdinand Kuhl
6908aad70c
All checks were successful
ci/woodpecker/push/code-style Pipeline was successful
151 lines
5.8 KiB
PHP
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;
|
|
}
|
|
}
|