<?php
namespace App\Controller\Api;
use Doctrine\DBAL\Connection;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
final class ShortLinkController extends AbstractController
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/** @Route("/api/public/links/resolve", name="api_short_link_resolve", methods={"GET"}) */
public function resolve(Request $request): JsonResponse
{
$url = trim((string) $request->query->get('url', ''));
if ($url === '' || filter_var($url, FILTER_VALIDATE_URL) === false) {
return $this->json(['message' => 'Invalid URL.'], 400);
}
$link = $this->connection->fetchAssociative(
'SELECT content_type, content_id, destination_url, status FROM short_link WHERE short_url = ? LIMIT 1',
[$url]
);
if (!$link) {
return $this->json(['message' => 'Link not found.'], 404);
}
if ($link['status'] !== 'active' || !$this->contentExists($link['content_type'], (int) $link['content_id'])) {
return $this->json([
'status' => 'deleted',
'destination' => rtrim((string) ($_ENV['PUBLIC_WEB_URL'] ?? 'https://preprod.inquize.net'), '/') . '/content-unavailable',
], 410);
}
return $this->json([
'status' => 'active',
'destination' => $link['destination_url'],
]);
}
/** @Route("/r/{code}", name="public_short_link_redirect", methods={"GET"}, requirements={"code"="[a-z0-9-]+"}) */
public function redirectLink(string $code): Response
{
$link = $this->connection->fetchAssociative(
'SELECT content_type, content_id, destination_url, provider_url, status FROM short_link WHERE provider = ? AND provider_code = ? LIMIT 1',
['zipquantum', $code]
);
if (!$link || $link['status'] !== 'active' || !$this->contentExists($link['content_type'], (int) $link['content_id'])) {
return new RedirectResponse('/content-unavailable', 302);
}
$target = filter_var($link['provider_url'], FILTER_VALIDATE_URL)
? $link['provider_url']
: $link['destination_url'];
return new RedirectResponse($target, 302);
}
/** @Route("/content-unavailable", name="public_content_unavailable", methods={"GET"}) */
public function unavailable(): Response
{
return new Response(
'<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Content unavailable ยท InQuize</title><body><main><h1>Content unavailable</h1><p>This InQuize content was removed or the link has expired.</p><p><a href="/">Return to InQuize</a></p></main></body></html>',
410,
['Content-Type' => 'text/html; charset=UTF-8', 'X-Robots-Tag' => 'noindex']
);
}
private function contentExists(string $type, int $id): bool
{
$tables = ['media' => 'media', 'program' => 'program', 'post' => 'post'];
if (!isset($tables[$type])) {
return false;
}
return (bool) $this->connection->fetchOne(
sprintf('SELECT 1 FROM %s WHERE id = ? AND is_deleted = 0', $tables[$type]),
[$id]
);
}
}