Files
Investidor-V2/tools/portal-qa-preserve-readable-data.php
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

69 lines
4.2 KiB
PHP

<?php
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\DB;
// Emergency read-only preservation, explicitly NOT a verified database backup.
require dirname(__DIR__).'/vendor/autoload.php';
require __DIR__.'/portal-qa-environment.php';
$target = portalQaEnvironment();
$app = require dirname(__DIR__).'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
portalQaAssertTarget($target);
$pdo = DB::connection()->getPdo();
$directory = storage_path('app/private/investmoz-v2/portal-qa-preservation/'.gmdate('Ymd_His'));
try {
$pdo->exec('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
$identity = $pdo->query("SELECT current_database() AS database, inet_server_port() AS port, current_setting('data_directory') AS data_directory")->fetch(PDO::FETCH_ASSOC);
if ((int) $identity['port'] !== 55441 || $identity['database'] !== $target['database']) {
throw new RuntimeException('QA identity changed.');
}
mkdir($directory, 0700, true);
$tables = [];
$quote = fn (string $name): string => '"'.str_replace('"', '""', $name).'"';
foreach ($pdo->query("SELECT schemaname,tablename FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,tablename")->fetchAll(PDO::FETCH_ASSOC) as $table) {
$name = $quote($table['schemaname']).'.'.$quote($table['tablename']);
$file = sprintf('%04d.ndjson', count($tables) + 1);
$stream = fopen($directory.'/'.$file, 'xb');
$statement = $pdo->query('SELECT row_to_json(t)::text FROM '.$name.' t ORDER BY row_to_json(t)::text');
$rows = 0;
while (($row = $statement->fetchColumn()) !== false) {
if (fwrite($stream, $row."\n") !== strlen($row) + 1) {
throw new RuntimeException('Private preservation write incomplete.');
}
$rows++;
}
fclose($stream);
$tables[$name] = ['file' => $file, 'rows' => $rows, 'sha256' => hash_file('sha256', $directory.'/'.$file)];
}
$sequences = [];
foreach ($pdo->query("SELECT schemaname,sequencename FROM pg_sequences WHERE schemaname NOT IN ('pg_catalog','information_schema') ORDER BY schemaname,sequencename")->fetchAll(PDO::FETCH_ASSOC) as $sequence) {
$name = $quote($sequence['schemaname']).'.'.$quote($sequence['sequencename']);
$sequences[$name] = $pdo->query('SELECT last_value,is_called FROM '.$name)->fetch(PDO::FETCH_ASSOC);
}
$missing = [];
foreach ($pdo->query("SELECT oid,relname,pg_relation_filepath(oid) AS path FROM pg_class WHERE relisshared AND relkind IN ('r','i','t') ORDER BY oid")->fetchAll(PDO::FETCH_ASSOC) as $relation) {
if (! is_file($identity['data_directory'].'/'.$relation['path'])) {
$missing[] = $relation;
}
}
$pdo->exec('ROLLBACK');
$proof = ['created_at' => gmdate('c'), 'target' => $identity, 'transaction_read_only' => true,
'tables' => $tables, 'sequences' => $sequences, 'missing_shared_catalog_files' => $missing,
'active_access' => false, 'database_writes' => 0, 'restore_verified' => false,
'scope' => 'Readable table bytes and sequence values preserved privately; schema/roles/catalog integrity and restoration NOT certified. Sequence values are not MVCC snapshots.'];
file_put_contents($directory.'/manifest.json', json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$receipt = ['created_at' => $proof['created_at'], 'target' => ['database' => $identity['database'], 'port' => $identity['port']],
'directory' => $directory, 'tables' => count($tables), 'rows' => array_sum(array_column($tables, 'rows')),
'sequences' => count($sequences), 'missing_shared_files' => count($missing),
'transaction_read_only' => true, 'database_writes' => 0, 'active_access' => false, 'restore_verified' => false];
file_put_contents(storage_path('app/private/investmoz-v2/portal-audit/qa-readable-preservation.json'), json_encode($receipt, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
echo json_encode($receipt, JSON_THROW_ON_ERROR).PHP_EOL;
} catch (Throwable $error) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
fwrite(STDERR, 'QA read-only preservation failed; no verified backup receipt produced.'.PHP_EOL);
exit(1);
}