url-shortener: QR encodes short URL with src=qr; forward query on redirect

QrHandler uses shortUrlForQr; RedirectHandler merges query string onto origin.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Anton Afanasyeu
2026-06-23 18:53:34 +02:00
parent ca4d54fa18
commit f06762d1e8
3 changed files with 14 additions and 2 deletions

View File

@@ -10,6 +10,12 @@ final class LinkRepository {
return self::publicBase() . '/' . $slug;
}
/** URL encoded in QR PNGs — always tagged for analytics. */
public static function shortUrlForQr(string $slug): string {
$url = self::shortUrl($slug);
return str_contains($url, '?') ? $url . '&src=qr' : $url . '?src=qr';
}
/** @return array<string,mixed>|null */
public static function findActiveByBearerOrigin(int $bearerId, string $originHash): ?array {
$pdo = Database::pdo();

View File

@@ -18,7 +18,7 @@ final class QrHandler {
}
$size = max(128, min(512, $size));
$target = LinkRepository::shortUrl($slug);
$target = LinkRepository::shortUrlForQr($slug);
$png = self::renderPng($target, $size);
if ($png === null) {
JsonResponse::error(503, 'INTERNAL', 'QR generation unavailable (install qrencode on BE)');

View File

@@ -12,8 +12,14 @@ final class RedirectHandler {
}
LinkRepository::recordRedirect($slug);
AuditLog::write('redirect', (int) $link['bearer_id'], $slug, '');
$dest = (string) $link['origin'];
$query = (string) ($_SERVER['QUERY_STRING'] ?? '');
if ($query !== '') {
$dest .= str_contains($dest, '?') ? '&' : '?';
$dest .= $query;
}
http_response_code(302);
header('Location: ' . (string) $link['origin']);
header('Location: ' . $dest);
exit;
}