68 lines
4.2 KiB
PHP
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;
|