mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-10 17:23:12 +08:00
Created new middlewares to track not found visits
This commit is contained in:
57
module/Core/src/ErrorHandler/Model/NotFoundType.php
Normal file
57
module/Core/src/ErrorHandler/Model/NotFoundType.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ErrorHandler\Model;
|
||||
|
||||
use Mezzio\Router\RouteResult;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Shlinkio\Shlink\Core\Action\RedirectAction;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
|
||||
use function rtrim;
|
||||
|
||||
final class NotFoundType
|
||||
{
|
||||
private string $type;
|
||||
|
||||
private function __construct(string $type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public static function fromRequest(ServerRequestInterface $request, string $basePath): self
|
||||
{
|
||||
$isBaseUrl = rtrim($request->getUri()->getPath(), '/') === $basePath;
|
||||
if ($isBaseUrl) {
|
||||
return new self(Visit::TYPE_BASE_URL);
|
||||
}
|
||||
|
||||
/** @var RouteResult $routeResult */
|
||||
$routeResult = $request->getAttribute(RouteResult::class, RouteResult::fromRouteFailure(null));
|
||||
if ($routeResult->isFailure()) {
|
||||
return new self(Visit::TYPE_REGULAR_404);
|
||||
}
|
||||
|
||||
if ($routeResult->getMatchedRouteName() === RedirectAction::class) {
|
||||
return new self(Visit::TYPE_INVALID_SHORT_URL);
|
||||
}
|
||||
|
||||
return new self(self::class);
|
||||
}
|
||||
|
||||
public function isBaseUrl(): bool
|
||||
{
|
||||
return $this->type === Visit::TYPE_BASE_URL;
|
||||
}
|
||||
|
||||
public function isRegularNotFound(): bool
|
||||
{
|
||||
return $this->type === Visit::TYPE_REGULAR_404;
|
||||
}
|
||||
|
||||
public function isInvalidShortUrl(): bool
|
||||
{
|
||||
return $this->type === Visit::TYPE_INVALID_SHORT_URL;
|
||||
}
|
||||
}
|
||||
@@ -4,67 +4,48 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ErrorHandler;
|
||||
|
||||
use Mezzio\Router\RouteResult;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Core\Action\RedirectAction;
|
||||
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
|
||||
use Shlinkio\Shlink\Core\Options;
|
||||
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
|
||||
|
||||
use function rtrim;
|
||||
|
||||
class NotFoundRedirectHandler implements MiddlewareInterface
|
||||
{
|
||||
private Options\NotFoundRedirectOptions $redirectOptions;
|
||||
private RedirectResponseHelperInterface $redirectResponseHelper;
|
||||
private string $shlinkBasePath;
|
||||
|
||||
public function __construct(
|
||||
Options\NotFoundRedirectOptions $redirectOptions,
|
||||
RedirectResponseHelperInterface $redirectResponseHelper,
|
||||
string $shlinkBasePath
|
||||
RedirectResponseHelperInterface $redirectResponseHelper
|
||||
) {
|
||||
$this->redirectOptions = $redirectOptions;
|
||||
$this->shlinkBasePath = $shlinkBasePath;
|
||||
$this->redirectResponseHelper = $redirectResponseHelper;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
/** @var RouteResult $routeResult */
|
||||
$routeResult = $request->getAttribute(RouteResult::class, RouteResult::fromRouteFailure(null));
|
||||
$redirectResponse = $this->createRedirectResponse($routeResult, $request->getUri());
|
||||
/** @var NotFoundType $notFoundType */
|
||||
$notFoundType = $request->getAttribute(NotFoundType::class);
|
||||
|
||||
return $redirectResponse ?? $handler->handle($request);
|
||||
}
|
||||
|
||||
private function createRedirectResponse(RouteResult $routeResult, UriInterface $uri): ?ResponseInterface
|
||||
{
|
||||
$isBaseUrl = rtrim($uri->getPath(), '/') === $this->shlinkBasePath;
|
||||
|
||||
if ($isBaseUrl && $this->redirectOptions->hasBaseUrlRedirect()) {
|
||||
if ($notFoundType->isBaseUrl() && $this->redirectOptions->hasBaseUrlRedirect()) {
|
||||
return $this->redirectResponseHelper->buildRedirectResponse($this->redirectOptions->getBaseUrlRedirect());
|
||||
}
|
||||
|
||||
if (!$isBaseUrl && $routeResult->isFailure() && $this->redirectOptions->hasRegular404Redirect()) {
|
||||
if ($notFoundType->isRegularNotFound() && $this->redirectOptions->hasRegular404Redirect()) {
|
||||
return $this->redirectResponseHelper->buildRedirectResponse(
|
||||
$this->redirectOptions->getRegular404Redirect(),
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$routeResult->isSuccess() &&
|
||||
$routeResult->getMatchedRouteName() === RedirectAction::class &&
|
||||
$this->redirectOptions->hasInvalidShortUrlRedirect()
|
||||
) {
|
||||
if ($notFoundType->isInvalidShortUrl() && $this->redirectOptions->hasInvalidShortUrlRedirect()) {
|
||||
return $this->redirectResponseHelper->buildRedirectResponse(
|
||||
$this->redirectOptions->getInvalidShortUrlRedirect(),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
|
||||
44
module/Core/src/ErrorHandler/NotFoundTrackerMiddleware.php
Normal file
44
module/Core/src/ErrorHandler/NotFoundTrackerMiddleware.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ErrorHandler;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
|
||||
use Shlinkio\Shlink\Core\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Visit\VisitsTrackerInterface;
|
||||
|
||||
class NotFoundTrackerMiddleware implements MiddlewareInterface
|
||||
{
|
||||
private VisitsTrackerInterface $visitsTracker;
|
||||
|
||||
public function __construct(VisitsTrackerInterface $visitsTracker)
|
||||
{
|
||||
$this->visitsTracker = $visitsTracker;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
/** @var NotFoundType $notFoundType */
|
||||
$notFoundType = $request->getAttribute(NotFoundType::class);
|
||||
$visitor = Visitor::fromRequest($request);
|
||||
|
||||
if ($notFoundType->isBaseUrl()) {
|
||||
$this->visitsTracker->trackBaseUrlVisit($visitor);
|
||||
}
|
||||
|
||||
if ($notFoundType->isRegularNotFound()) {
|
||||
$this->visitsTracker->trackRegularNotFoundVisit($visitor);
|
||||
}
|
||||
|
||||
if ($notFoundType->isInvalidShortUrl()) {
|
||||
$this->visitsTracker->trackInvalidShortUrlVisit($visitor);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Core\ErrorHandler;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
|
||||
|
||||
class NotFoundTypeResolverMiddleware implements MiddlewareInterface
|
||||
{
|
||||
private string $shlinkBasePath;
|
||||
|
||||
public function __construct(string $shlinkBasePath)
|
||||
{
|
||||
$this->shlinkBasePath = $shlinkBasePath;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$notFoundType = NotFoundType::fromRequest($request, $this->shlinkBasePath);
|
||||
return $handler->handle($request->withAttribute(NotFoundType::class, $notFoundType));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user