docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -8,6 +8,8 @@ use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Waf\DetectionCategory;
use App\Framework\Waf\DetectionSeverity;
use PDO;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\ValueObjects\SqlQuery;
/**
* Database implementation of the feedback repository
@@ -18,7 +20,7 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
* @param PDO $pdo Database connection
*/
public function __construct(
private PDO $pdo
private readonly ConnectionInterface $connection
) {
}
@@ -27,14 +29,14 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
*/
public function saveFeedback(DetectionFeedback $feedback): void
{
$stmt = $this->pdo->prepare('
$sql = '
INSERT INTO waf_feedback (
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
');
';
$stmt->execute([
$this->connection->execute(SqlQuery::create($sql, [
$feedback->detectionId,
$feedback->feedbackType->value,
$feedback->userId,
@@ -43,7 +45,7 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
$feedback->category->value,
$feedback->severity->value,
json_encode($feedback->context),
]);
]));
}
/**
@@ -51,18 +53,18 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
*/
public function getFeedbackForDetection(string $detectionId): array
{
$stmt = $this->pdo->prepare('
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
WHERE detection_id = ?
ORDER BY timestamp DESC
');
';
$stmt->execute([$detectionId]);
$result = $this->connection->query(SqlQuery::create($sql, [$detectionId]));
return $this->hydrateMultipleResults($stmt);
return $this->hydrateMultipleResults($result);
}
/**
@@ -87,10 +89,9 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
$sql .= ' ORDER BY timestamp DESC';
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$result = $this->connection->query(SqlQuery::create($sql, $params));
return $this->hydrateMultipleResults($stmt);
return $this->hydrateMultipleResults($result);
}
/**
@@ -115,10 +116,9 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
$sql .= ' ORDER BY timestamp DESC';
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$result = $this->connection->query(SqlQuery::create($sql, $params));
return $this->hydrateMultipleResults($stmt);
return $this->hydrateMultipleResults($result);
}
/**
@@ -127,44 +127,44 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
public function getFeedbackStats(): array
{
// Get total count
$totalStmt = $this->pdo->query('SELECT COUNT(*) FROM waf_feedback');
$totalCount = (int)$totalStmt->fetchColumn();
$totalResult = $this->connection->query(SqlQuery::create('SELECT COUNT(*) FROM waf_feedback'));
$totalCount = (int)$totalResult->fetchColumn();
// Get counts by feedback type
$typeStmt = $this->pdo->query('
$typeResult = $this->connection->query(SqlQuery::create('
SELECT feedback_type, COUNT(*) as count
FROM waf_feedback
GROUP BY feedback_type
');
'));
$typeStats = [];
while ($row = $typeStmt->fetch(PDO::FETCH_ASSOC)) {
foreach ($typeResult->fetchAll() as $row) {
$typeStats[$row['feedback_type']] = (int)$row['count'];
}
// Get counts by category
$categoryStmt = $this->pdo->query('
$categoryResult = $this->connection->query(SqlQuery::create('
SELECT category, COUNT(*) as count
FROM waf_feedback
GROUP BY category
');
'));
$categoryStats = [];
while ($row = $categoryStmt->fetch(PDO::FETCH_ASSOC)) {
foreach ($categoryResult->fetchAll() as $row) {
$categoryStats[$row['category']] = (int)$row['count'];
}
// Get counts by severity
$severityStmt = $this->pdo->query('
$severityResult = $this->connection->query(SqlQuery::create('
SELECT severity, COUNT(*) as count
FROM waf_feedback
GROUP BY severity
');
'));
$severityStats = [];
while ($row = $severityStmt->fetch(PDO::FETCH_ASSOC)) {
foreach ($severityResult->fetchAll() as $row) {
$severityStats[$row['severity']] = (int)$row['count'];
}
// Get trend data (last 30 days)
$trendStmt = $this->pdo->query('
$trendResult = $this->connection->query(SqlQuery::create('
SELECT
DATE(timestamp) as date,
feedback_type,
@@ -173,9 +173,9 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(timestamp), feedback_type
ORDER BY date
');
'));
$trendData = [];
while ($row = $trendStmt->fetch(PDO::FETCH_ASSOC)) {
foreach ($trendResult->fetchAll() as $row) {
if (! isset($trendData[$row['date']])) {
$trendData[$row['date']] = [];
}
@@ -196,18 +196,18 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
*/
public function getRecentFeedback(int $limit = 10): array
{
$stmt = $this->pdo->prepare('
$sql = '
SELECT
detection_id, feedback_type, user_id, comment,
timestamp, category, severity, context
FROM waf_feedback
ORDER BY timestamp DESC
LIMIT ?
');
';
$stmt->execute([$limit]);
$result = $this->connection->query(SqlQuery::create($sql, [$limit]));
return $this->hydrateMultipleResults($stmt);
return $this->hydrateMultipleResults($result);
}
/**
@@ -216,11 +216,11 @@ final readonly class DatabaseFeedbackRepository implements FeedbackRepositoryInt
* @param \PDOStatement $stmt The executed PDO statement
* @return DetectionFeedback[] Array of DetectionFeedback objects
*/
private function hydrateMultipleResults(\PDOStatement $stmt): array
private function hydrateMultipleResults($result): array
{
$results = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
foreach ($result->fetchAll() as $row) {
$results[] = $this->hydrateFromRow($row);
}

View File

@@ -9,6 +9,7 @@ use App\Framework\DateTime\Clock;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Performance\PerformanceService;
use App\Framework\Waf\Layers\CommandInjectionLayer;
use App\Framework\Waf\Layers\PathTraversalLayer;
@@ -55,16 +56,16 @@ final readonly class WafEngineInitializer
// Register core security layers in priority order
$this->registerSecurityLayers();
$this->logger->info('WAF Engine initialized successfully', [
$this->logger->info('WAF Engine initialized successfully', LogContext::withData([
'registered_layers' => $this->getRegisteredLayerNames(),
'health_status' => $this->wafEngine->getHealthStatus(),
]);
]));
} catch (\Throwable $e) {
$this->logger->error('Failed to initialize WAF Engine', [
$this->logger->error('Failed to initialize WAF Engine', LogContext::withData([
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
]));
// Re-throw to prevent application startup with broken WAF
throw $e;
@@ -89,9 +90,9 @@ final readonly class WafEngineInitializer
// Low priority layers (processed last)
$this->wafEngine->registerLayer(new SuspiciousUserAgentLayer());
$this->logger->debug('Security layers registered', [
$this->logger->debug('Security layers registered', LogContext::withData([
'layers_count' => count($this->getRegisteredLayerNames()),
]);
]));
}
/**