Files
michaelschiemer/src/Framework/Waf/Feedback/DatabaseFeedbackRepository.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

250 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Waf\Feedback;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
use App\Framework\Waf\DetectionCategory;
use App\Framework\Waf\DetectionSeverity;
use PDO;
/**
* Database implementation of the feedback repository
*/
final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInterface
{
/**
* @param PDO $pdo Database connection
*/
public function __construct(
private readonly ConnectionInterface $connection
) {
}
/**
* {@inheritdoc}
*/
public function saveFeedback(DetectionFeedback $feedback): void
{
$sql = '
INSERT INTO waf_feedback (
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
';
$this->connection->execute(SqlQuery::create($sql, [
$feedback->detectionId,
$feedback->feedbackType->value,
$feedback->userId,
$feedback->comment,
$feedback->timestamp->toSqlString(),
$feedback->category->value,
$feedback->severity->value,
json_encode($feedback->context),
]));
}
/**
* {@inheritdoc}
*/
public function getFeedbackForDetection(string $detectionId): array
{
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
WHERE detection_id = ?
ORDER BY timestamp DESC
';
$result = $this->connection->query(SqlQuery::create($sql, [$detectionId]));
return $this->hydrateMultipleResults($result);
}
/**
* {@inheritdoc}
*/
public function getFeedbackByCategory(DetectionCategory $category, ?Timestamp $since = null): array
{
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
WHERE category = ?
';
$params = [$category->value];
if ($since !== null) {
$sql .= ' AND timestamp >= ?';
$params[] = $since->toSqlString();
}
$sql .= ' ORDER BY timestamp DESC';
$result = $this->connection->query(SqlQuery::create($sql, $params));
return $this->hydrateMultipleResults($result);
}
/**
* {@inheritdoc}
*/
public function getFeedbackByFeedbackType(FeedbackType $feedbackType, ?Timestamp $since = null): array
{
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
WHERE feedback_type = ?
';
$params = [$feedbackType->value];
if ($since !== null) {
$sql .= ' AND timestamp >= ?';
$params[] = $since->toSqlString();
}
$sql .= ' ORDER BY timestamp DESC';
$result = $this->connection->query(SqlQuery::create($sql, $params));
return $this->hydrateMultipleResults($result);
}
/**
* {@inheritdoc}
*/
public function getFeedbackStats(): array
{
// Get total count
$totalResult = $this->connection->query(SqlQuery::create('SELECT COUNT(*) FROM waf_feedback'));
$totalCount = (int)$totalResult->fetchColumn();
// Get counts by feedback type
$typeResult = $this->connection->query(SqlQuery::create('
SELECT feedback_type, COUNT(*) as count
FROM waf_feedback
GROUP BY feedback_type
'));
$typeStats = [];
foreach ($typeResult->fetchAll() as $row) {
$typeStats[$row['feedback_type']] = (int)$row['count'];
}
// Get counts by category
$categoryResult = $this->connection->query(SqlQuery::create('
SELECT category, COUNT(*) as count
FROM waf_feedback
GROUP BY category
'));
$categoryStats = [];
foreach ($categoryResult->fetchAll() as $row) {
$categoryStats[$row['category']] = (int)$row['count'];
}
// Get counts by severity
$severityResult = $this->connection->query(SqlQuery::create('
SELECT severity, COUNT(*) as count
FROM waf_feedback
GROUP BY severity
'));
$severityStats = [];
foreach ($severityResult->fetchAll() as $row) {
$severityStats[$row['severity']] = (int)$row['count'];
}
// Get trend data (last 30 days)
$trendResult = $this->connection->query(SqlQuery::create('
SELECT
DATE(timestamp) as date,
feedback_type,
COUNT(*) as count
FROM waf_feedback
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(timestamp), feedback_type
ORDER BY date
'));
$trendData = [];
foreach ($trendResult->fetchAll() as $row) {
if (! isset($trendData[$row['date']])) {
$trendData[$row['date']] = [];
}
$trendData[$row['date']][$row['feedback_type']] = (int)$row['count'];
}
return [
'total_count' => $totalCount,
'by_feedback_type' => $typeStats,
'by_category' => $categoryStats,
'by_severity' => $severityStats,
'trend_data' => $trendData,
];
}
/**
* {@inheritdoc}
*/
public function getRecentFeedback(int $limit = 10): array
{
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
ORDER BY timestamp DESC
LIMIT ?
';
$result = $this->connection->query(SqlQuery::create($sql, [$limit]));
return $this->hydrateMultipleResults($result);
}
/**
* Hydrates multiple DetectionFeedback objects from a PDO statement
*
* @param \PDOStatement $stmt The executed PDO statement
* @return DetectionFeedback[] Array of DetectionFeedback objects
*/
private function hydrateMultipleResults($result): array
{
$results = [];
foreach ($result->fetchAll() as $row) {
$results[] = $this->hydrateFromRow($row);
}
return $results;
}
/**
* Hydrates a DetectionFeedback object from a database row
*
* @param array $row Database row
* @return DetectionFeedback
*/
private function hydrateFromRow(array $row): DetectionFeedback
{
return new DetectionFeedback(
detectionId: $row['detection_id'],
feedbackType: FeedbackType::from($row['feedback_type']),
userId: $row['user_id'],
comment: $row['comment'],
timestamp: Timestamp::fromString($row['timestamp']),
category: DetectionCategory::from($row['category']),
severity: DetectionSeverity::from($row['severity']),
context: json_decode($row['context'] ?? '{}', true) ?: []
);
}
}