This commit is contained in:
2026-11-03 11:51:44 +02:00
parent badaada043
commit e753f7a0a4
309 changed files with 14537 additions and 529 deletions
@@ -0,0 +1,63 @@
<?php
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Process\Process;
require dirname(__DIR__).'/vendor/autoload.php';
$root = dirname(__DIR__);
if (!in_array('--apply-authorized', $argv, true)) { throw new LogicException('Explicit authorized installation required.'); }
$s = json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/payables-demo-compras.json'), true, 32, JSON_THROW_ON_ERROR);
if (($s['APP_ENV'] ?? '') !== 'payable-testing' || ($s['DB_HOST'] ?? '') !== '127.0.0.1' || realpath($s['HOST_ROOT'] ?? '') !== realpath($root) || !preg_match('/^compras_receipts_test_([a-f0-9]{12})$/D', $s['DB_DATABASE'] ?? '', $m) || $s['DB_USERNAME'] !== 'payable_qa_'.$m[1]) { throw new LogicException('Wrong pilot destination.'); }
$app = require $root.'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
$owner = DB::connection('pgsql')->getConfig(); unset($owner['name']);
if ($owner['host'] !== '127.0.0.1') { throw new LogicException('Local server required.'); }
$qa = [...$owner, 'url'=>null, 'database'=>$s['DB_DATABASE'], 'username'=>$s['DB_USERNAME'], 'password'=>$s['DB_PASSWORD']];
config(['database.connections.receipt_install'=>$qa, 'database.connections.receipt_owner'=>[...$owner,'url'=>null,'database'=>'postgres']]);
DB::setDefaultConnection('receipt_install');
$db = DB::connection(); $admin = DB::connection('receipt_owner');
$identity = $db->selectOne('select current_database() as db, current_user as role');
if ($identity->db !== $s['DB_DATABASE'] || $identity->role !== $s['DB_USERNAME']) { throw new LogicException('Identity mismatch.'); }
$proofPath = $root.'/docs/proofs/receipt-block-installation.json';
if (file_exists($proofPath)) { throw new LogicException('Installation proof exists; inspect before repeating.'); }
$tables = array_column($db->select("select tablename from pg_tables where schemaname='public' order by tablename"),'tablename');
if (in_array('receipt_documents',$tables,true)) { throw new LogicException('Document table already exists.'); }
$fingerprint = function($connection,$tables) { $out=[]; foreach($tables as $table) { $rows=$connection->select('select row_to_json(t)::text as row from "'.str_replace('"','""',$table).'" t order by row_to_json(t)::text'); $out[$table]=hash('sha256',json_encode(array_column($rows,'row'),JSON_THROW_ON_ERROR)); } return $out; };
$pg = function($binary,$args,$connection) { $p=new Process(['C:/Program Files/PostgreSQL/17/bin/'.$binary.'.exe',...$args],null,['PGHOST'=>$connection['host'],'PGPORT'=>(string)$connection['port'],'PGUSER'=>$connection['username'],'PGPASSWORD'=>$connection['password']],timeout:120); $p->run(); if(!$p->isSuccessful()) { throw new RuntimeException('Backup/restore failed.'); } };
$restore='compras_pilot_restore_'.bin2hex(random_bytes(6)); $created=false;
try {
$db->beginTransaction(); $db->statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');
$baseline=$fingerprint($db,$tables);
$directory='C:/laragon/data/sgf-tempo/backups/compras-receipt-documents-'.gmdate('Ymd-His').'-'.bin2hex(random_bytes(3));
if(!mkdir($directory,0700,true)) { throw new RuntimeException('Backup directory unavailable.'); }
$dump=$directory.'/before.dump';
$snapshot=$db->selectOne('select pg_export_snapshot() as snapshot')->snapshot;
$pg('pg_dump',['--format=custom','--snapshot='.$snapshot,'--file='.$dump,'--dbname='.$qa['database']],$qa);
$db->commit();
$admin->statement('CREATE DATABASE "'.$restore.'"'); $created=true;
$pg('pg_restore',['--no-owner','--no-privileges','--exit-on-error','--dbname='.$restore,$dump],$owner);
config(['database.connections.receipt_restore'=>[...$owner,'url'=>null,'database'=>$restore]]);
if($fingerprint(DB::connection('receipt_restore'),$tables)!==$baseline) { throw new LogicException('Restored backup differs.'); }
$disk=Storage::disk('receipt_documents'); $probe='.installation-'.bin2hex(random_bytes(8));
try { $disk->put($probe,'private-storage-probe'); if($disk->get($probe)!=='private-storage-probe') { throw new RuntimeException('Storage unavailable.'); } } finally { $disk->delete($probe); }
$migration='database/migrations/2026_11_03_000002_create_receipt_documents.php';
$db->transaction(function() use($db,$tables,$baseline,$fingerprint,$migration) {
foreach($tables as $table) { $db->statement('LOCK TABLE "'.str_replace('"','""',$table).'" IN SHARE MODE'); }
if($fingerprint($db,$tables)!==$baseline) { throw new LogicException('Pilot changed after backup; abort.'); }
if(Artisan::call('migrate',['--database'=>'receipt_install','--path'=>$migration,'--force'=>true])!==0) { throw new RuntimeException('Target migration failed.'); }
$protected=array_values(array_diff($tables,['migrations']));
if($fingerprint($db,$protected)!==array_intersect_key($baseline,array_flip($protected))) { throw new LogicException('Business data changed.'); }
if($db->table('receipt_documents')->count()!==0 || $db->table('migrations')->where('migration','2026_11_03_000001_add_two_factor_columns_to_users')->exists()) { throw new LogicException('Unexpected installation state.'); }
});
$proof=['checked_at'=>gmdate('c'),'environment'=>'http://127.0.0.1:8005','result'=>'passed','authorization'=>'User explicitly authorized receipt document block installation','migration'=>$migration,'backup'=>$dump,'backup_sha256'=>hash_file('sha256',$dump),'restored_tables_checked'=>count($tables),'protected_tables_checked'=>count($tables)-1,'business_data_unchanged'=>true,'documents_created'=>0,'mfa_enabled'=>false,'restore_database_removed'=>false];
} finally {
if($db->transactionLevel()>0) { $db->rollBack(); }
DB::purge('receipt_restore');
if($created) { $admin->statement('DROP DATABASE "'.$restore.'" WITH (FORCE)'); }
}
$proof['restore_database_removed']=true;
file_put_contents($proofPath,json_encode($proof,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES).PHP_EOL);
echo 'Receipt documents installed; backup restored and business data preserved.'.PHP_EOL;