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_COLOR = '#000000'; // Black
|
||||||
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
||||||
const IP_ADDRESS_REQUEST_ATTRIBUTE = 'remote_address';
|
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\ShortUrl\ShortUrlResolverInterface;
|
||||||
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
|
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
|
||||||
|
|
||||||
|
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||||
|
|
||||||
abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMethodInterface
|
abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMethodInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -30,9 +32,13 @@ abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMet
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$shortUrl = $this->urlResolver->resolveEnabledShortUrl($identifier);
|
$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) {
|
} catch (ShortUrlNotFoundException) {
|
||||||
return $this->createErrorResp($request, $handler);
|
return $this->createErrorResp($request, $handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use Psr\Http\Server\MiddlewareInterface;
|
|||||||
use Psr\Http\Server\RequestHandlerInterface;
|
use Psr\Http\Server\RequestHandlerInterface;
|
||||||
use Shlinkio\Shlink\Core\Visit\RequestTrackerInterface;
|
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)
|
public function __construct(private RequestTrackerInterface $requestTracker)
|
||||||
{
|
{
|
||||||
@@ -18,7 +20,12 @@ class NotFoundTrackerMiddleware implements MiddlewareInterface
|
|||||||
|
|
||||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||||
{
|
{
|
||||||
$this->requestTracker->trackNotFoundIfApplicable($request);
|
$response = $handler->handle($request);
|
||||||
return $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 sprintf;
|
||||||
use function trim;
|
use function trim;
|
||||||
|
|
||||||
|
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||||
|
|
||||||
readonly class ExtraPathRedirectMiddleware implements MiddlewareInterface
|
readonly class ExtraPathRedirectMiddleware implements MiddlewareInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -73,9 +75,12 @@ readonly class ExtraPathRedirectMiddleware implements MiddlewareInterface
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$shortUrl = $this->resolver->resolveEnabledShortUrl($identifier);
|
$shortUrl = $this->resolver->resolveEnabledShortUrl($identifier);
|
||||||
$this->requestTracker->trackIfApplicable($shortUrl, $request);
|
|
||||||
|
|
||||||
$longUrl = $this->redirectionBuilder->buildShortUrlRedirect($shortUrl, $request, $extraPath);
|
$longUrl = $this->redirectionBuilder->buildShortUrlRedirect($shortUrl, $request, $extraPath);
|
||||||
|
$this->requestTracker->trackIfApplicable(
|
||||||
|
$shortUrl,
|
||||||
|
$request->withAttribute(REDIRECT_URL_REQUEST_ATTRIBUTE, $longUrl),
|
||||||
|
);
|
||||||
|
|
||||||
return $this->redirectResponseHelper->buildRedirectResponse($longUrl);
|
return $this->redirectResponseHelper->buildRedirectResponse($longUrl);
|
||||||
} catch (ShortUrlNotFoundException) {
|
} catch (ShortUrlNotFoundException) {
|
||||||
if ($extraPath === null || ! $this->urlShortenerOptions->multiSegmentSlugsEnabled) {
|
if ($extraPath === null || ! $this->urlShortenerOptions->multiSegmentSlugsEnabled) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class Visit extends AbstractEntity implements JsonSerializable
|
|||||||
potentialBot: $visitor->potentialBot,
|
potentialBot: $visitor->potentialBot,
|
||||||
remoteAddr: self::processAddress($visitor->remoteAddress, $anonymize),
|
remoteAddr: self::processAddress($visitor->remoteAddress, $anonymize),
|
||||||
visitedUrl: $visitor->visitedUrl,
|
visitedUrl: $visitor->visitedUrl,
|
||||||
redirectUrl: null, // TODO
|
redirectUrl: $visitor->redirectUrl,
|
||||||
visitLocation: $geolocation !== null ? VisitLocation::fromGeolocation($geolocation) : null,
|
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\ipAddressFromRequest;
|
||||||
use function Shlinkio\Shlink\Core\isCrawler;
|
use function Shlinkio\Shlink\Core\isCrawler;
|
||||||
use function substr;
|
use function substr;
|
||||||
|
use const Shlinkio\Shlink\REDIRECT_URL_REQUEST_ATTRIBUTE;
|
||||||
|
|
||||||
final readonly class Visitor
|
final readonly class Visitor
|
||||||
{
|
{
|
||||||
@@ -28,7 +29,7 @@ final readonly class Visitor
|
|||||||
public string $visitedUrl,
|
public string $visitedUrl,
|
||||||
public bool $potentialBot,
|
public bool $potentialBot,
|
||||||
public Location|null $geolocation,
|
public Location|null $geolocation,
|
||||||
public string $redirectUrl,
|
public string|null $redirectUrl,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ final readonly class Visitor
|
|||||||
string|null $remoteAddress = null,
|
string|null $remoteAddress = null,
|
||||||
string $visitedUrl = '',
|
string $visitedUrl = '',
|
||||||
Location|null $geolocation = null,
|
Location|null $geolocation = null,
|
||||||
string $redirectUrl = '',
|
string|null $redirectUrl = null,
|
||||||
): self {
|
): self {
|
||||||
return new self(
|
return new self(
|
||||||
userAgent: self::cropToLength($userAgent, self::USER_AGENT_MAX_LENGTH),
|
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),
|
visitedUrl: self::cropToLength($visitedUrl, self::VISITED_URL_MAX_LENGTH),
|
||||||
potentialBot: isCrawler($userAgent),
|
potentialBot: isCrawler($userAgent),
|
||||||
geolocation: $geolocation,
|
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),
|
remoteAddress: ipAddressFromRequest($request),
|
||||||
visitedUrl: $request->getUri()->__toString(),
|
visitedUrl: $request->getUri()->__toString(),
|
||||||
geolocation: geolocationFromRequest($request),
|
geolocation: geolocationFromRequest($request),
|
||||||
// TODO
|
redirectUrl: $request->getAttribute(REDIRECT_URL_REQUEST_ATTRIBUTE),
|
||||||
redirectUrl: '',
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user