src/Controller/Ai/AcademicSearchHistoryController.php line 35

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Ai;
  3. use App\Repository\AcademicSearchHistoryRepository;
  4. use App\Service\Ai\PluginAuthResolver;
  5. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  6. use Symfony\Component\HttpFoundation\JsonResponse;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Symfony\Component\Routing\Annotation\Route;
  9. /**
  10.  * Historique de recherche académique pour l'utilisateur courant.
  11.  *
  12.  * Endpoints (Phase 19+) :
  13.  *  - GET    /api/ai/academic/history?source=arxiv          → 10 dernières
  14.  *  - DELETE /api/ai/academic/history/{id}                  → supprime un item
  15.  *  - DELETE /api/ai/academic/history?source=arxiv          → vide une source
  16.  *  - DELETE /api/ai/academic/history                       → vide tout
  17.  *
  18.  * Auth : cookie session OU JWT Bearer (cf. PluginAuthResolver).
  19.  */
  20. #[Route('/api/ai/academic')]
  21. class AcademicSearchHistoryController extends AbstractController
  22. {
  23.     /** Sources autorisées (anti-injection). */
  24.     private const ALLOWED_SOURCES = ['arxiv''openalex''crossref''pubmed''hal''semantic-scholar'];
  25.     public function __construct(
  26.         private readonly AcademicSearchHistoryRepository $repo,
  27.         private readonly PluginAuthResolver $authResolver,
  28.     ) {}
  29.     #[Route('/history'name'ai_academic_history_list'methods: ['GET'], options: ['expose' => true])]
  30.     public function list(Request $request): JsonResponse
  31.     {
  32.         $user $this->authResolver->resolve($request);
  33.         if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
  34.         $source = (string) $request->query->get('source''');
  35.         if (!in_array($sourceself::ALLOWED_SOURCEStrue)) {
  36.             return new JsonResponse(['error' => 'invalid_source''allowed' => self::ALLOWED_SOURCES], 400);
  37.         }
  38.         $limit min(50max(1, (int) $request->query->get('limit'10)));
  39.         $entries $this->repo->findRecent($user$source$limit);
  40.         return new JsonResponse([
  41.             'source' => $source,
  42.             'history' => array_map(fn($e) => [
  43.                 'id' => $e->getId(),
  44.                 'query' => $e->getQuery(),
  45.                 'hitCount' => $e->getHitCount(),
  46.                 'lastUsedAt' => $e->getLastUsedAt()->format(\DateTimeInterface::ATOM),
  47.                 'createdAt' => $e->getCreatedAt()->format(\DateTimeInterface::ATOM),
  48.             ], $entries),
  49.         ]);
  50.     }
  51.     #[Route('/history/{id}'name'ai_academic_history_delete'methods: ['DELETE'], requirements: ['id' => '\d+'], options: ['expose' => true])]
  52.     public function delete(int $idRequest $request): JsonResponse
  53.     {
  54.         $user $this->authResolver->resolve($request);
  55.         if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
  56.         $ok $this->repo->deleteForUser($id$user);
  57.         if (!$ok) return new JsonResponse(['error' => 'not_found'], 404);
  58.         return new JsonResponse(['deleted' => true]);
  59.     }
  60.     #[Route('/history'name'ai_academic_history_clear'methods: ['DELETE'], options: ['expose' => true])]
  61.     public function clear(Request $request): JsonResponse
  62.     {
  63.         $user $this->authResolver->resolve($request);
  64.         if (!$user) return new JsonResponse(['error' => 'unauthorized'], 401);
  65.         $source $request->query->get('source');
  66.         if ($source !== null && !in_array($sourceself::ALLOWED_SOURCEStrue)) {
  67.             return new JsonResponse(['error' => 'invalid_source'], 400);
  68.         }
  69.         $count $this->repo->clearForUser($user$source);
  70.         return new JsonResponse(['deleted' => $count]);
  71.     }
  72. }