/shared/platform-tools/backups/restore_runner.php * * Via CLI : * php shared/platform-tools/backups/restore_runner.php [opções] * Opções: --dry-run apenas valida e mostra o que faria * --no-safety não tira backup de segurança (NÃO recomendado) * --purge (só ficheiros) apaga o que não estiver no artefacto * --list lista os backups disponíveis e sai * * Via HTTP: POST restore_runner.php com token=&file= * GET é recusado de propósito: restaurar não é uma operação idempotente de * leitura e não deve ser possível dispará-la com um link. * * TODO RESTAURO É DESTRUTIVO. Por omissão tira sempre um backup do estado * actual primeiro e devolve o nome desse ficheiro no relatório. */ require_once __DIR__ . '/config.php'; require_once __DIR__ . '/../functions_restore.php'; $IS_CLI = (php_sapi_name() === 'cli'); // --- Autenticação ----------------------------------------------------------- if (!$IS_CLI) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); header('Content-Type: application/json'); die(json_encode(['ok' => false, 'error' => 'Use POST.'])); } $token = $_POST['token'] ?? ''; if (BACKUP_TOKEN === '' || BACKUP_TOKEN === 'replace_me' || !hash_equals(BACKUP_TOKEN, (string) $token)) { http_response_code(403); header('Content-Type: application/json'); die(json_encode(['ok' => false, 'error' => 'Acesso negado.'])); } ignore_user_abort(true); set_time_limit(0); } // --- Argumentos ------------------------------------------------------------- if ($IS_CLI) { $args = array_slice($argv, 1); $flags = array_values(array_filter($args, fn($a) => str_starts_with($a, '--'))); $files = array_values(array_filter($args, fn($a) => !str_starts_with($a, '--'))); $file = $files[0] ?? ''; $dryRun = in_array('--dry-run', $flags, true); $safety = !in_array('--no-safety', $flags, true); $purge = in_array('--purge', $flags, true); $list = in_array('--list', $flags, true); } else { $file = (string) ($_POST['file'] ?? ''); $dryRun = !empty($_POST['dry_run']); $safety = ($_POST['safety'] ?? '1') !== '0'; $purge = !empty($_POST['purge']); $list = !empty($_POST['list']); } function out(array $data, bool $cli): void { if (!$cli) { header('Content-Type: application/json'); echo json_encode($data, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); return; } foreach ($data as $k => $v) { if (is_array($v)) $v = $v ? implode(', ', array_map('strval', $v)) : '(nenhum)'; elseif (is_bool($v)) $v = $v ? 'sim' : 'não'; elseif ($v === null) $v = '-'; printf(" %-16s %s\n", $k, $v); } } if ($list) { $rows = backup_list(); if ($IS_CLI) { echo "Backups disponíveis em " . backup_dir() . ":\n"; foreach ($rows as $r) printf(" %-46s %-6s %s\n", $r['filename'], $r['type'], $r['size_human']); if (!$rows) echo " (nenhum)\n"; } else { header('Content-Type: application/json'); echo json_encode(['ok'=>true,'backups'=>$rows]); } exit; } if ($file === '') { out(['ok' => false, 'error' => 'Indique o ficheiro a restaurar (ou --list).'], $IS_CLI); exit($IS_CLI ? 1 : 0); } // --- Validação (sempre, mesmo fora do dry-run) ------------------------------ $info = restore_inspect($file); if ($IS_CLI) { echo "Artefacto : {$file}\n"; echo "Tipo : " . ($info['type'] ?? '?') . "\n"; echo "Tamanho : " . ($info['size_human'] ?? '?') . "\n"; echo "Entradas : " . ($info['entries'] ?? 0) . "\n\n"; } if (!$info['ok']) { out(['ok' => false, 'error' => $info['error']], $IS_CLI); exit($IS_CLI ? 1 : 0); } if ($dryRun) { out(['ok' => true, 'dry_run' => true, 'type' => $info['type'], 'would_restore' => $info['type'] === 'db' ? 'base de dados ' . DB_NAME : (defined('BACKUP_FILES_DIR') ? BACKUP_FILES_DIR : 'uploads'), 'safety_backup' => $safety ? 'sim (antes de escrever)' : 'NÃO', 'purge' => $purge], $IS_CLI); exit; } // --- Execução --------------------------------------------------------------- if ($info['type'] === 'db') { try { $dsn = 'mysql:host=' . DB_HOST . ';port=' . DB_PORT . ';dbname=' . DB_NAME . ';charset=' . DB_CHARSET; $pdo = new PDO($dsn, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, ]); } catch (PDOException $e) { out(['ok' => false, 'error' => 'Ligação à BD falhou: ' . $e->getMessage()], $IS_CLI); exit($IS_CLI ? 1 : 0); } $report = restore_db($pdo, $file, ['safety' => $safety]); } else { $report = restore_files($file, ['safety' => $safety, 'purge' => $purge]); } out($report, $IS_CLI); exit($IS_CLI ? ($report['ok'] ? 0 : 1) : 0);