- AppReport entity + migration (type, message, fingerprint SHA-256, created_at) - POST /api/report avec rate-limit 5/heure par fingerprint IP - ReportModal : formulaire type radio (bug/idée) + textarea 1 000 chars max - ChangelogModal : fonctionnalités en prod, roadmap, 5 limitations honnêtes - Deux boutons flottants bas-gauche (? info + ✉ signalement) - Légende remontée pour éviter le chevauchement Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
68 lines
2.2 KiB
PHP
68 lines
2.2 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\AppReport;
|
|
use Doctrine\DBAL\Connection;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
|
|
#[Route('/api/report', name: 'api_report_')]
|
|
class ReportController extends AbstractController
|
|
{
|
|
public function __construct(
|
|
private EntityManagerInterface $em,
|
|
private Connection $connection,
|
|
) {}
|
|
|
|
/**
|
|
* POST /api/report
|
|
*
|
|
* Body : { "type": "bug"|"idea", "message": "..." }
|
|
* Rate limit : 5 reports/heure par fingerprint (hash IP).
|
|
*/
|
|
#[Route('', name: 'create', methods: ['POST'])]
|
|
public function create(Request $request): JsonResponse
|
|
{
|
|
$body = json_decode($request->getContent(), true);
|
|
|
|
$type = trim((string) ($body['type'] ?? ''));
|
|
$message = trim((string) ($body['message'] ?? ''));
|
|
|
|
if (!in_array($type, ['bug', 'idea'], true)) {
|
|
return $this->json(['error' => 'type must be "bug" or "idea"'], 400);
|
|
}
|
|
if (strlen($message) < 5) {
|
|
return $this->json(['error' => 'message too short (min 5 chars)'], 400);
|
|
}
|
|
if (strlen($message) > 1000) {
|
|
return $this->json(['error' => 'message too long (max 1000 chars)'], 400);
|
|
}
|
|
|
|
$fingerprint = hash('sha256', $request->getClientIp() ?? 'unknown');
|
|
|
|
// Rate limit : 5 rapports par heure par fingerprint
|
|
$count = (int) $this->connection->fetchOne(
|
|
"SELECT COUNT(*) FROM app_report
|
|
WHERE fingerprint = :fp AND created_at >= NOW() - INTERVAL '1 hour'",
|
|
['fp' => $fingerprint]
|
|
);
|
|
if ($count >= 5) {
|
|
return $this->json(['error' => 'Rate limit exceeded. Try again later.'], 429);
|
|
}
|
|
|
|
$report = new AppReport();
|
|
$report->setType($type);
|
|
$report->setMessage($message);
|
|
$report->setFingerprint($fingerprint);
|
|
|
|
$this->em->persist($report);
|
|
$this->em->flush();
|
|
|
|
return $this->json(['ok' => true], 201);
|
|
}
|
|
}
|