argument('backupJobId')); $token = $this->argument('token'); $statusFile = sys_get_temp_dir() . '/' . $token . '.json'; $artifact = $sourceJob?->artifact_path; if (!$artifact || !file_exists($artifact)) { $this->writeStatus($statusFile, 'failed', ['✗ Archivdatei nicht gefunden: ' . $artifact]); return self::FAILURE; } $this->writeStatus($statusFile, 'running', ['Archiv wird extrahiert…']); $extractDir = sys_get_temp_dir() . '/mailwolt_restore_' . $token; mkdir($extractDir, 0700, true); $log = []; $exitCode = 0; // ── 1. Archiv extrahieren ───────────────────────────────────────── $tarOut = []; $tarExit = 0; exec( "tar --ignore-failed-read -xzf " . escapeshellarg($artifact) . " -C " . escapeshellarg($extractDir) . " 2>&1", $tarOut, $tarExit ); if ($tarExit > 1) { $log[] = '✗ Extraktion fehlgeschlagen: ' . implode('; ', array_slice($tarOut, -3)); $exitCode = 1; } else { $log[] = '✓ Archiv extrahiert'; } $this->writeStatus($statusFile, 'running', $log); // ── 2. Datenbank ───────────────────────────────────────────────── $dbFiles = []; exec("find " . escapeshellarg($extractDir) . " -name 'database.sql' -type f 2>/dev/null", $dbFiles); if (!empty($dbFiles)) { $log[] = 'Datenbank wird importiert…'; $this->writeStatus($statusFile, 'running', $log); [$ok, $msg] = $this->importDatabase($dbFiles[0]); $log[] = $ok ? '✓ Datenbank wiederhergestellt' : '✗ Datenbank: ' . $msg; if (!$ok) $exitCode = 1; } else { $log[] = '— Kein Datenbank-Dump im Archiv'; } $this->writeStatus($statusFile, 'running', $log); // ── 3. E-Mails (Maildirs) ──────────────────────────────────────── foreach (["{$extractDir}/var/mail", "{$extractDir}/var/vmail"] as $mailSrc) { if (is_dir($mailSrc)) { $log[] = 'E-Mails werden wiederhergestellt…'; $this->writeStatus($statusFile, 'running', $log); $destParent = '/' . implode('/', array_slice(explode('/', $mailSrc, -1 + substr_count($mailSrc, '/')), 1, -1)); $cpOut = []; $cpExit = 0; exec("cp -rp " . escapeshellarg($mailSrc) . " " . escapeshellarg($destParent) . "/ 2>&1", $cpOut, $cpExit); $log[] = $cpExit === 0 ? '✓ E-Mails wiederhergestellt' : '✗ Mails: ' . implode('; ', array_slice($cpOut, -3)); if ($cpExit !== 0) $exitCode = 1; } } // ── 4. Konfiguration ───────────────────────────────────────────── $etcSrc = "{$extractDir}/etc"; if (is_dir($etcSrc)) { $log[] = 'Konfiguration wird wiederhergestellt…'; $this->writeStatus($statusFile, 'running', $log); foreach (scandir($etcSrc) ?: [] as $entry) { if ($entry === '.' || $entry === '..') continue; $cpOut = []; $cpExit = 0; exec("cp -rp " . escapeshellarg("{$etcSrc}/{$entry}") . " /etc/ 2>&1", $cpOut, $cpExit); $log[] = $cpExit === 0 ? '✓ /etc/' . $entry : '— /etc/' . $entry . ': ' . implode('; ', array_slice($cpOut, -1)); } } // ── 5. Dienste neu laden ───────────────────────────────────────── foreach (['postfix', 'dovecot'] as $svc) { if (is_dir("{$etcSrc}/{$svc}")) { $restOut = []; exec("systemctl reload-or-restart {$svc} 2>&1", $restOut, $restExit); $log[] = $restExit === 0 ? '✓ ' . $svc . ' neu geladen' : '— ' . $svc . ': ' . implode('; ', $restOut); } } // ── Aufräumen ──────────────────────────────────────────────────── exec("rm -rf " . escapeshellarg($extractDir)); $finalStatus = $exitCode === 0 ? 'ok' : 'failed'; $this->writeStatus($statusFile, $finalStatus, $log); return $exitCode === 0 ? self::SUCCESS : self::FAILURE; } private function importDatabase(string $sqlFile): array { $conn = config('database.connections.' . config('database.default')); if (($conn['driver'] ?? '') !== 'mysql') { return [false, 'Nur MySQL/MariaDB wird unterstützt.']; } $cmd = 'MYSQL_PWD=' . escapeshellarg($conn['password'] ?? '') . ' mysql' . ' -h ' . escapeshellarg($conn['host'] ?? '127.0.0.1') . ' -P ' . escapeshellarg((string)($conn['port'] ?? '3306')) . ' -u ' . escapeshellarg($conn['username'] ?? '') . ' ' . escapeshellarg($conn['database'] ?? '') . ' < ' . escapeshellarg($sqlFile) . ' 2>&1'; $output = []; $exit = 0; exec($cmd, $output, $exit); return $exit === 0 ? [true, ''] : [false, implode('; ', array_slice($output, -3))]; } private function writeStatus(string $file, string $status, array $log): void { file_put_contents($file, json_encode([ 'status' => $status, 'log' => implode("\n", $log), 'timestamp' => time(), ])); } }