64 lines
6.0 KiB
PHP
64 lines
6.0 KiB
PHP
<?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;
|