Files
Investidor-V2/tools/partnership-backup-verify.php
T
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

68 lines
4.2 KiB
PHP

<?php
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\DB;
require __DIR__.'/../vendor/autoload.php';
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
$active = DB::connection();
$identity = $active->selectOne('select current_database() as database, inet_server_port() as port');
if ($identity->database !== 'investemoz' || (int) $identity->port !== 5432) {
throw new RuntimeException('Unexpected active backup target.');
}
$directory = storage_path('app/private/partnership-exchange/backup-'.date('Ymd-His'));
mkdir($directory, 0700, true);
$dump = $directory.'/database.dump';
$qaName = 'partnership_restore_'.date('Ymd_His').'_test';
$bin = 'C:/Program Files/PostgreSQL/17/bin/';
$run = function (array $command, array $environment = []) {
$process = proc_open($command, [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, null, [...getenv(), ...$environment]);
if (! is_resource($process)) {
throw new RuntimeException('Backup process unavailable.');
}
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$errors = stream_get_contents($pipes[2]);
fclose($pipes[2]);
if (proc_close($process) !== 0) {
throw new RuntimeException('Backup/restore command failed: '.$errors);
}
return $output;
};
$proof = $active->transaction(function () use ($active, $run, $bin, $qaName, $dump, $identity) {
$active->statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY');
$snapshot = $active->selectOne('select pg_export_snapshot() as snapshot')->snapshot;
$run([$bin.'pg_dump.exe', '-h', $active->getConfig('host'), '-p', '5432', '-U', $active->getConfig('username'), '-d', 'investemoz', '-Fc', '--snapshot='.$snapshot, '-f', $dump], ['PGPASSWORD' => $active->getConfig('password')]);
$run([$bin.'createdb.exe', '-h', '127.0.0.1', '-p', '55441', '-U', 'postgres', $qaName], ['PGPASSWORD' => '']);
$run([$bin.'pg_restore.exe', '-h', '127.0.0.1', '-p', '55441', '-U', 'postgres', '-d', $qaName, '--exit-on-error', '--no-owner', '--no-privileges', $dump], ['PGPASSWORD' => '']);
config(['database.connections.partnership_restore' => [...config('database.connections.pgsql'), 'host' => '127.0.0.1', 'port' => 55441, 'database' => $qaName, 'username' => 'postgres', 'password' => '', 'url' => null]]);
$restore = DB::connection('partnership_restore');
$restored = $restore->selectOne('select current_database() as database, inet_server_port() as port');
if ($restored->database !== $qaName || ! str_ends_with($qaName, '_test') || (int) $restored->port !== 55441) {
throw new RuntimeException('QA identity rejected.');
}
$counts = [];
$hashes = [];
foreach ($active->select("select tablename from pg_tables where schemaname='public' order by tablename") as $table) {
$name = $table->tablename;
$quoted = '"'.str_replace('"', '""', $name).'"';
$rows = fn ($connection) => array_map(fn ($row) => $row->value, $connection->select('select row_to_json(t)::text as value from public.'.$quoted.' as t order by row_to_json(t)::text'));
$a = $rows($active);
$b = $rows($restore);
if ($a !== $b) {
throw new RuntimeException('Restored contents differ: '.$name);
}
$counts[$name] = count($a);
$hashes[$name] = hash('sha256', implode("\n", $a));
}
return ['restore_verified' => true, 'verified_at' => now()->toIso8601String(), 'target' => $identity, 'restored_target' => $restored,
'dump_path' => $dump, 'sha256' => hash_file('sha256', $dump), 'table_counts' => $counts, 'table_hashes' => $hashes];
});
file_put_contents(storage_path('app/private/partnership-exchange/backup-proof.json'), json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$audit = $active->table('audit_events')->where('source_system', 'documented-relations-repair')->get(['actor_user_id', 'created_at', 'action']);
echo json_encode(['restore_verified' => true, 'tables' => count($proof['table_counts']), 'project_locations' => $proof['table_counts']['project_locations'], 'restored_target' => $proof['restored_target'], 'relationship_audit' => $audit], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR).PHP_EOL;