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); } }