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

61 lines
6.4 KiB
PHP

<?php
// Actual destination READ ONLY; no providers, mail, job consumption or migrations.
ini_set('zend.exception_ignore_args', '1');
set_exception_handler(static function (Throwable $e): void {
fwrite(STDERR, 'Activation preflight stopped: '.$e::class.' at '.basename($e->getFile()).':'.$e->getLine().PHP_EOL); exit(1);
});
$root=dirname(__DIR__);$private=$root.'/storage/app/private/investmoz-v2/portal-audit';
require $root.'/vendor/autoload.php';
$candidate=json_decode(file_get_contents($private.'/consolidated-candidate-latest.json'),true,512,JSON_THROW_ON_ERROR);
$backup=json_decode(file_get_contents($private.'/workflow-execution-target-backup.json'),true,512,JSON_THROW_ON_ERROR);
$compat=json_decode(file_get_contents($private.'/workflow-execution-preflight-qa.json'),true,512,JSON_THROW_ON_ERROR);
$manifest=json_decode(file_get_contents($candidate['output'].'/manifesto.json'),true,512,JSON_THROW_ON_ERROR);
throw_unless($candidate['revision']==='41caea0f900fe0705e84e065a65f9534ff3a585ee34098f860666f522875a3a5'
&& hash_file('sha256',$candidate['output'].'/'.$candidate['zip'])==='1d6dece89817459f012909f309f688e85e40752e845469855b2dce101879c102'
&& hash_file('sha256',$candidate['output'].'/manifesto.json')==='092ea57b5eeecef8ae1c44f4c1a8dd6c95d63af5e4448524c46a9e251b1d9b63',RuntimeException::class,'Authorized candidate mismatch');
throw_unless($backup['result']==='passed' && $compat['result']==='passed' && $backup['candidate_revision']===$candidate['revision']
&& $backup['candidate_zip_sha256']===$candidate['zip_sha256'] && $backup['restore_verified'] && $backup['files_verified']
&& strtotime($backup['verified_at'])>=time()-86400 && strtotime($backup['verified_at'])<=time()
&& hash_file('sha256',$backup['dump_path'])===$backup['dump_sha256'] && hash_file('sha256',$backup['files_path'])===$backup['files_sha256'],RuntimeException::class,'Backup/recovery evidence expired or differs');
foreach($manifest['base_hashes'] as $name=>$expected)throw_unless((is_file($root.'/'.$name)?hash_file('sha256',$root.'/'.$name):null)===$expected,RuntimeException::class,'Active code baseline differs');
foreach($manifest['unchanged_dependencies'] as $name=>$expected)throw_unless(hash_file('sha256',$root.'/'.$name)===$expected,RuntimeException::class,'Unchanged dependency differs');
$app=require $root.'/bootstrap/app.php';
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
$config=config('database.connections.'.config('database.default'));
throw_unless($config['driver']==='pgsql' && empty($config['url']) && in_array($config['host'],['127.0.0.1','localhost'],true)
&& (int)$config['port']===5432 && $config['database']==='investemoz' && ! $app->configurationIsCached(),RuntimeException::class);
throw_unless(config('app.url')==='https://portal-investidor.test' && config('portal.organization_country_enabled')
&& !config('portal.notifications_enabled') && count(config('portal.notification_recipients',[]))===0,RuntimeException::class);
foreach(['portal.profile_access_enabled','portal.opportunity_dashboard_access_enabled','portal.opportunity_specialized_drafts_enabled',
'portal.opportunity_modular_profiles_qa_enabled','portal.legacy_content_qa_enabled','investor-path.enabled'] as $flag)throw_unless(!config($flag),RuntimeException::class,'Operational flag must remain disabled');
$pdo=new PDO('pgsql:host='.$config['host'].';port=5432;dbname=investemoz',$config['username'],$config['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
$pdo->exec('BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY');
$identity=$pdo->query("SELECT current_database() AS database,inet_server_port() AS port,current_setting('transaction_read_only') AS read_only")->fetch(PDO::FETCH_ASSOC);
throw_unless($identity['database']==='investemoz' && (int)$identity['port']===5432 && $identity['read_only']==='on',RuntimeException::class);
$tables=(int)$pdo->query("SELECT count(*) FROM pg_tables WHERE schemaname NOT IN ('pg_catalog','information_schema')")->fetchColumn();
throw_unless($tables===288,RuntimeException::class,'Destination schema no longer matches previous state');
$changed=[];$transient=['"public"."sessions"','"public"."cache"','"public"."cache_locks"'];
foreach($backup['tables'] as $table=>$expected){
$rows=$pdo->query('SELECT row_to_json(t)::text FROM '.$table.' t ORDER BY row_to_json(t)::text')->fetchAll(PDO::FETCH_COLUMN);
if(count($rows)!==$expected['count'] || hash('sha256',implode("\n",$rows))!==$expected['sha256']){
throw_unless(in_array($table,$transient,true),RuntimeException::class,'Non-transient target data changed after backup; renew before activation');$changed[]=$table;
}
}
$holders=$pdo->query("SELECT id,email,email_verified_at IS NOT NULL AS verified,two_factor_secret IS NOT NULL AND two_factor_confirmed_at IS NOT NULL AS mfa FROM users WHERE id IN(2,13) ORDER BY id")->fetchAll(PDO::FETCH_ASSOC);
throw_unless(count($holders)===2 && (int)$holders[0]['id']===2 && $holders[0]['email']==='nala@tempo.co.mz'
&& (int)$holders[1]['id']===13 && $holders[1]['email']==='onala@me.com' && $holders[0]['verified'] && $holders[1]['verified'] && $holders[0]['mfa'] && $holders[1]['mfa'],RuntimeException::class,'Owner-designated existing holder differs');
$jobs=(int)$pdo->query('SELECT count(*) FROM jobs')->fetchColumn();$failed=(int)$pdo->query('SELECT count(*) FROM failed_jobs')->fetchColumn();
$pdo->exec('ROLLBACK');
throw_unless($jobs===0 && $failed===0,RuntimeException::class,'Queue differs; inspect without consuming');
$receipt=['at'=>gmdate('c'),'result'=>'passed_before_any_deployment','candidate_revision'=>$candidate['revision'],
'zip_sha256'=>$candidate['zip_sha256'],'manifest_sha256'=>$candidate['manifest_sha256'],'target'=>$identity,
'backup_verified_at'=>$backup['verified_at'],'backup_file_count'=>867,'source_schema_tables'=>288,
'original_non_transient_target_rows_match_backup'=>true,'transient_tables_changed'=>$changed,
'COUNTRY02_enabled'=>true,'operational_flags_enabled'=>false,'external_notifications_enabled'=>false,
'queue'=>['jobs'=>$jobs,'failed_jobs'=>$failed,'consumed'=>0], 'holder_ids'=>[2,13],
'user_authorized_in_this_turn'=>['candidate41caea0f900f','B1_B2','authenticated030000_then040000','crossed_bootstrap2and13'],
'active_database_writes'=>0,'service_or_acl_changes'=>0];
file_put_contents($private.'/workflow-activation-preflight.json',json_encode($receipt,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo json_encode($receipt,JSON_THROW_ON_ERROR).PHP_EOL;