55 lines
3.0 KiB
PHP
55 lines
3.0 KiB
PHP
<?php
|
|
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
require dirname(__DIR__).'/vendor/autoload.php';
|
|
$app = require dirname(__DIR__).'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
$active = DB::connection();
|
|
if ($active->getDatabaseName() !== 'investemoz' || (string) $active->getConfig('port') !== '5432') {
|
|
throw new RuntimeException('Unexpected active target.');
|
|
}
|
|
config(['database.connections.anchor_restore' => [...config('database.connections.pgsql'), 'host' => '127.0.0.1', 'port' => '55441',
|
|
'database' => 'anchor_restore_20261007_test', 'username' => 'postgres', 'password' => '', 'url' => null]]);
|
|
$restore = DB::connection('anchor_restore');
|
|
$identity = $restore->selectOne('select current_database() as database, inet_server_port() as port');
|
|
if ($identity->database !== 'anchor_restore_20261007_test' || (int) $identity->port !== 55441) {
|
|
throw new RuntimeException('Restore verification requires isolated QA.');
|
|
}
|
|
$report = $active->transaction(function () use ($active, $restore) {
|
|
$active->statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY');
|
|
$counts = [];
|
|
foreach ($active->select("select tablename from pg_tables where schemaname='public' order by tablename") as $table) {
|
|
$name = $table->tablename;
|
|
$a = $active->table($name)->count();
|
|
$b = $restore->table($name)->count();
|
|
if ($a !== $b) {
|
|
throw new RuntimeException('Restore count differs for '.$name);
|
|
}
|
|
$counts[$name] = $a;
|
|
}
|
|
$hashes = [];
|
|
foreach (['projects', 'economic_clusters', 'investment_dossiers', 'project_locations', 'cluster_projects', 'opportunity_projects', 'opportunity_clusters', 'value_chains', 'value_chain_links', 'opportunities', 'geo_locations', 'geo_geometries'] as $table) {
|
|
$hash = function ($db) use ($table) {
|
|
$rows = array_map(fn ($row) => $row->value, $db->select('select row_to_json(t)::text as value from public.'.$table.' as t'));
|
|
sort($rows, SORT_STRING);
|
|
|
|
return hash('sha256', implode("\n", $rows));
|
|
};
|
|
$a = $hash($active);
|
|
if ($a !== $hash($restore)) {
|
|
throw new RuntimeException('Restore data differs for '.$table);
|
|
}
|
|
$hashes[$table] = $a;
|
|
}
|
|
|
|
return ['table_counts' => $counts, 'relation_table_hashes' => $hashes];
|
|
});
|
|
$directory = storage_path('app/private/anchor-project-reconciliation');
|
|
$dump = $directory.'/backup-20261007/database.dump';
|
|
$proof = [...$report, 'restore_verified' => true, 'verified_at' => date(DATE_ATOM), 'target' => ['database' => 'investemoz', 'port' => 5432],
|
|
'restored_target' => ['database' => $identity->database, 'port' => $identity->port], 'dump_path' => $dump, 'sha256' => hash_file('sha256', $dump)];
|
|
file_put_contents($directory.'/backup-proof.json', json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
|
echo 'Restore verified: '.count($report['table_counts']).' table counts and '.count($report['relation_table_hashes']).' content hashes.'.PHP_EOL;
|