mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Save where a visitor is redirected for any kind of tracked visit
This commit is contained in:
@@ -22,3 +22,4 @@ const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = true;
|
||||
const DEFAULT_QR_CODE_COLOR = '#000000'; // Black
|
||||
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
||||
const IP_ADDRESS_REQUEST_ATTRIBUTE = 'remote_address';
|
||||
const REDIRECT_URL_REQUEST_ATTRIBUTE = 'redirect_url';
|
||||
|
||||
@@ -16,6 +16,8 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface;
|
||||
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
|
||||
|
||||
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||
|
||||
abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMethodInterface
|
||||
{
|
||||
public function __construct(
|
||||
@@ -30,9 +32,13 @@ abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMet
|
||||
|
||||
try {
|
||||
$shortUrl = $this->urlResolver->resolveEnabledShortUrl($identifier);
|
||||
$this->requestTracker->trackIfApplicable($shortUrl, $request);
|
||||
$response = $this->createSuccessResp($shortUrl, $request);
|
||||
$this->requestTracker->trackIfApplicable($shortUrl, $request->withAttribute(
|
||||
REDIRECT_URL_REQUEST_ATTRIBUTE,
|
||||
$response->hasHeader('Location') ? $response->getHeaderLine('Location') : null,
|
||||
));
|
||||
|
||||
return $this->createSuccessResp($shortUrl, $request);
|
||||
return $response;
|
||||
} catch (ShortUrlNotFoundException) {
|
||||
return $this->createErrorResp($request, $handler);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
|
||||
|
||||
class NotFoundTrackerMiddleware implements MiddlewareInterface
|
||||
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||
|
||||
readonly class NotFoundTrackerMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function __construct(private RequestTrackerInterface $requestTracker)
|
||||
{
|
||||
@@ -18,7 +20,12 @@ class NotFoundTrackerMiddleware implements MiddlewareInterface
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$this->requestTracker->trackNotFoundIfApplicable($request);
|
||||
return $handler->handle($request);
|
||||
$response = $handler->handle($request);
|
||||
$this->requestTracker->trackNotFoundIfApplicable($request->withAttribute(
|
||||
REDIRECT_URL_REQUEST_ATTRIBUTE,
|
||||
$response->hasHeader('Location') ? $response->getHeaderLine('Location') : null,
|
||||
));
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ use function implode;
|
||||
use function sprintf;
|
||||
use function trim;
|
||||
|
||||
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||
|
||||
readonly class ExtraPathRedirectMiddleware implements MiddlewareInterface
|
||||
{
|
||||
public function __construct(
|
||||
@@ -73,9 +75,12 @@ readonly class ExtraPathRedirectMiddleware implements MiddlewareInterface
|
||||
|
||||
try {
|
||||
$shortUrl = $this->resolver->resolveEnabledShortUrl($identifier);
|
||||
$this->requestTracker->trackIfApplicable($shortUrl, $request);
|
||||
|
||||
$longUrl = $this->redirectionBuilder->buildShortUrlRedirect($shortUrl, $request, $extraPath);
|
||||
$this->requestTracker->trackIfApplicable(
|
||||
$shortUrl,
|
||||
$request->withAttribute(REDIRECT_URL_REQUEST_ATTRIBUTE, $longUrl),
|
||||
);
|
||||
|
||||
return $this->redirectResponseHelper->buildRedirectResponse($longUrl);
|
||||
} catch (ShortUrlNotFoundException) {
|
||||
if ($extraPath === null || ! $this->urlShortenerOptions->multiSegmentSlugsEnabled) {
|
||||
|
||||
@@ -69,7 +69,7 @@ class Visit extends AbstractEntity implements JsonSerializable
|
||||
potentialBot: $visitor->potentialBot,
|
||||
remoteAddr: self::processAddress($visitor->remoteAddress, $anonymize),
|
||||
visitedUrl: $visitor->visitedUrl,
|
||||
redirectUrl: null, // TODO
|
||||
redirectUrl: $visitor->redirectUrl,
|
||||
visitLocation: $geolocation !== null ? VisitLocation::fromGeolocation($geolocation) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use function Shlinkio\Shlink\Core\geolocationFromRequest;
|
||||
use function Shlinkio\Shlink\Core\ipAddressFromRequest;
|
||||
use function Shlinkio\Shlink\Core\isCrawler;
|
||||
use function substr;
|
||||
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||
|
||||
final readonly class Visitor
|
||||
{
|
||||
@@ -28,7 +29,7 @@ final readonly class Visitor
|
||||
public string $visitedUrl,
|
||||
public bool $potentialBot,
|
||||
public Location|null $geolocation,
|
||||
public string $redirectUrl,
|
||||
public string|null $redirectUrl,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ final readonly class Visitor
|
||||
string|null $remoteAddress = null,
|
||||
string $visitedUrl = '',
|
||||
Location|null $geolocation = null,
|
||||
string $redirectUrl = '',
|
||||
string|null $redirectUrl = null,
|
||||
): self {
|
||||
return new self(
|
||||
userAgent: self::cropToLength($userAgent, self::USER_AGENT_MAX_LENGTH),
|
||||
@@ -49,7 +50,7 @@ final readonly class Visitor
|
||||
visitedUrl: self::cropToLength($visitedUrl, self::VISITED_URL_MAX_LENGTH),
|
||||
potentialBot: isCrawler($userAgent),
|
||||
geolocation: $geolocation,
|
||||
redirectUrl: self::cropToLength($redirectUrl, self::REDIRECT_URL_MAX_LENGTH),
|
||||
redirectUrl: $redirectUrl === null ? null : self::cropToLength($redirectUrl, self::REDIRECT_URL_MAX_LENGTH),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,8 +67,7 @@ final readonly class Visitor
|
||||
remoteAddress: ipAddressFromRequest($request),
|
||||
visitedUrl: $request->getUri()->__toString(),
|
||||
geolocation: geolocationFromRequest($request),
|
||||
// TODO
|
||||
redirectUrl: '',
|
||||
redirectUrl: $request->getAttribute(REDIRECT_URL_REQUEST_ATTRIBUTE),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user