Backup #116

ID116
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/Persistence/FileBackupRepository.php
Version1
Typ modified
Größe6.3 KB
Hashc7ed5a2664c8cbde82027d58c9f4e208b473b8d7ae2ecd27983f8818351dec66
Datum2025-12-20 19:24:12
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\Persistence;

use Infrastructure\Config\DatabaseFactory;
use PDO;

/**
 * Repository for file_backup_history table.
 */
class FileBackupRepository
{
    private PDO $db;

    public function __construct()
    {
        $this->db = DatabaseFactory::dev();
    }

    /**
     * Find all backups with optional filters.
     *
     * @param array<string, mixed> $filters
     */
    public function findAll(array $filters = [], int $limit = 50, int $offset = 0): array
    {
        $sql = 'SELECT id, file_path, content_hash, file_size, version, change_type,
                       changed_at, changed_by, reason
                FROM file_backup_history WHERE 1=1';
        $params = [];

        if (!empty($filters['search'])) {
            $sql .= ' AND file_path LIKE :search';
            $params['search'] = '%' . $filters['search'] . '%';
        }

        if (!empty($filters['change_type'])) {
            $sql .= ' AND change_type = :change_type';
            $params['change_type'] = $filters['change_type'];
        }

        $sql .= ' ORDER BY changed_at DESC LIMIT :limit OFFSET :offset';

        $stmt = $this->db->prepare($sql);
        foreach ($params as $key => $value) {
            $stmt->bindValue($key, $value);
        }
        $stmt->bindValue('limit', $limit, PDO::PARAM_INT);
        $stmt->bindValue('offset', $offset, PDO::PARAM_INT);
        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Count total backups with filters.
     *
     * @param array<string, mixed> $filters
     */
    public function count(array $filters = []): int
    {
        $sql = 'SELECT COUNT(*) FROM file_backup_history WHERE 1=1';
        $params = [];

        if (!empty($filters['search'])) {
            $sql .= ' AND file_path LIKE :search';
            $params['search'] = '%' . $filters['search'] . '%';
        }

        if (!empty($filters['change_type'])) {
            $sql .= ' AND change_type = :change_type';
            $params['change_type'] = $filters['change_type'];
        }

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);

        return (int) $stmt->fetchColumn();
    }

    /**
     * Find backup by ID.
     */
    public function findById(int $id): ?array
    {
        $stmt = $this->db->prepare(
            'SELECT * FROM file_backup_history WHERE id = :id'
        );
        $stmt->execute(['id' => $id]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);

        return $result !== false ? $result : null;
    }

    /**
     * Find all backups for a specific file path.
     */
    public function findByFilePath(string $path): array
    {
        $stmt = $this->db->prepare(
            'SELECT id, file_path, content_hash, file_size, version, change_type,
                    changed_at, changed_by, reason
             FROM file_backup_history
             WHERE file_path = :path
             ORDER BY version DESC'
        );
        $stmt->execute(['path' => $path]);

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Get statistics about backups.
     *
     * @return array<string, int>
     */
    public function getStatistics(): array
    {
        $stats = [];

        // Total backups
        $stats['total'] = (int) $this->db->query(
            'SELECT COUNT(*) FROM file_backup_history'
        )->fetchColumn();

        // Unique files
        $stats['files'] = (int) $this->db->query(
            'SELECT COUNT(DISTINCT file_path) FROM file_backup_history'
        )->fetchColumn();

        // Total versions (max version per file summed)
        $stats['versions'] = (int) $this->db->query(
            'SELECT COALESCE(SUM(max_version), 0) FROM (
                SELECT MAX(version) as max_version FROM file_backup_history GROUP BY file_path
            ) as v'
        )->fetchColumn();

        // Last 24 hours
        $stats['recent'] = (int) $this->db->query(
            'SELECT COUNT(*) FROM file_backup_history WHERE changed_at >= NOW() - INTERVAL 24 HOUR'
        )->fetchColumn();

        // By change type
        $stats['created'] = (int) $this->db->query(
            "SELECT COUNT(*) FROM file_backup_history WHERE change_type = 'created'"
        )->fetchColumn();

        $stats['modified'] = (int) $this->db->query(
            "SELECT COUNT(*) FROM file_backup_history WHERE change_type = 'modified'"
        )->fetchColumn();

        return $stats;
    }

    /**
     * Restore a file from backup.
     *
     * @throws \RuntimeException If restore fails
     */
    public function restore(int $id): bool
    {
        $backup = $this->findById($id);

        if ($backup === null) {
            throw new \RuntimeException('Backup not found');
        }

        $filePath = $backup['file_path'];
        $content = $backup['file_content'];

        // Check if directory exists
        $dir = dirname($filePath);
        if (!is_dir($dir)) {
            throw new \RuntimeException("Directory does not exist: {$dir}");
        }

        // Write content back to file
        $result = file_put_contents($filePath, $content);

        if ($result === false) {
            throw new \RuntimeException("Failed to write to file: {$filePath}");
        }

        // Log the restore action
        $this->logRestore($id, $filePath);

        return true;
    }

    /**
     * Log restore action to mcp_log.
     */
    private function logRestore(int $backupId, string $filePath): void
    {
        $stmt = $this->db->prepare(
            "INSERT INTO mcp_log (tool, operation, parameters, result, logged_at)
             VALUES ('backup_restore', 'restore', :params, 'success', NOW())"
        );
        $stmt->execute([
            'params' => json_encode(['backup_id' => $backupId, 'file_path' => $filePath]),
        ]);
    }

    /**
     * Get content preview (first N lines).
     */
    public function getContentPreview(int $id, int $maxLines = 500): ?string
    {
        $backup = $this->findById($id);

        if ($backup === null || empty($backup['file_content'])) {
            return null;
        }

        $lines = explode("\n", $backup['file_content']);

        if (count($lines) <= $maxLines) {
            return $backup['file_content'];
        }

        return implode("\n", array_slice($lines, 0, $maxLines)) . "\n\n... (" . (count($lines) - $maxLines) . ' weitere Zeilen)';
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
133 4 modified 8.6 KB 2025-12-20 19:53
129 3 modified 8.6 KB 2025-12-20 19:40
117 2 modified 6.6 KB 2025-12-20 19:24
116 1 modified 6.3 KB 2025-12-20 19:24

← Zurück zur Übersicht