50 lines
6.3 KiB
Python
50 lines
6.3 KiB
Python
"""Reuse the established read-only dump/QA restoration algorithm for the workflow target."""
|
|
from pathlib import Path
|
|
root=Path(__file__).resolve().parent.parent
|
|
s=(root/'tools/portal-country-candidate-backup.php').read_text(encoding='utf8')
|
|
s=s.replace('use App\\Services\\OrganizationCountryInstallation;','')
|
|
s=s.replace("fwrite(STDERR, 'Candidate backup/restore not completed; no active writes. Examine the private command diagnostic.\\n');", """file_put_contents(dirname(__DIR__).'/storage/app/private/investmoz-v2/portal-audit/functional-scope-target-backup-failure-20261011.json',json_encode(['type'=>$error::class,'file'=>basename($error->getFile()),'line'=>$error->getLine(),'message'=>preg_replace('/(?:base64:)?[A-Za-z0-9+\\\\/=]{40,}/','[REDACTED]',$error->getMessage())],JSON_THROW_ON_ERROR));
|
|
fwrite(STDERR, 'Target preparation not completed; sanitized private diagnostic retained; no active writes.');""")
|
|
s=s.replace("$hash = (new OrganizationCountryInstallation)->fingerprint();", """$scope=json_decode(file_get_contents($root.'/storage/app/private/investmoz-v2/portal-audit/functional-delivery-scope-code-20261011.json'),true,512,JSON_THROW_ON_ERROR);
|
|
$hash=$scope['code_revision'];
|
|
foreach($scope['active_base_hashes']as$name=>$expected){throw_unless((is_file($root.'/'.$name)?hash_file('sha256',$root.'/'.$name):null)===$expected,RuntimeException::class,'Active baseline changed before snapshot.');}""")
|
|
s=s.replace('country_candidate_', 'workflow_target_')
|
|
s=s.replace("organization-country-installation/", "portal-audit/workflow-target-backups/")
|
|
marker="$source->exec('ROLLBACK');\n$restored->exec('ROLLBACK');"
|
|
assert s.count(marker)==1
|
|
s=s.replace(marker,"""$encryptionKeys=array_values(array_unique(array_filter([config('app.key'),...config('app.previous_keys',[])])));
|
|
$encrypters=array_map(fn($key)=>new Illuminate\\Encryption\\Encrypter(str_starts_with($key,'base64:')?base64_decode(substr($key,7),true):$key,config('app.cipher')),$encryptionKeys);
|
|
$mfaReadable=0;
|
|
foreach($restored->query('SELECT two_factor_secret,two_factor_recovery_codes FROM users WHERE two_factor_secret IS NOT NULL')->fetchAll(PDO::FETCH_ASSOC)as$row){
|
|
foreach(['two_factor_secret','two_factor_recovery_codes']as$field){if($row[$field]===null)continue;$readable=false;foreach($encrypters as$crypt){try{$plain=$crypt->decrypt($row[$field],false);$readable=is_string($plain)&&$plain!=='';unset($plain);if($readable)break;}catch(Illuminate\\Contracts\\Encryption\\DecryptException){}}
|
|
throw_unless($readable,RuntimeException::class,'An active MFA envelope is not recoverable with the preserved configured keys.');}$mfaReadable++;
|
|
}
|
|
throw_unless($installationState['country_flag'] && count($installationState['country_columns'])===2 && $installationState['migrations']===['2026_10_10_050000_extend_organization_country'],RuntimeException::class,'Preserved COUNTRY02/dependency baseline differs.');
|
|
"""+marker)
|
|
s=s.replace("'config_cached' => $app->configurationIsCached()],", "'config_cached' => $app->configurationIsCached(), 'previous_keys'=>config('app.previous_keys',[])],")
|
|
marker='$zip->close();\n$read = new ZipArchive;'
|
|
assert s.count(marker)==1
|
|
s=s.replace(marker,"""// Code, configuration and TLS are privately preserved; no active file is replaced.
|
|
foreach(['app','config','routes','bootstrap']as$folder){foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root.'/'.$folder,FilesystemIterator::SKIP_DOTS))as$file){if(!$file->isFile())continue;$relative=str_replace('\\\\','/',substr($file->getPathname(),strlen($root)+1));$entry='active-code/'.$relative;$zip->addFile($file->getPathname(),$entry);$fileHashes[$entry]=hash_file('sha256',$file->getPathname());}}
|
|
foreach(['composer.json','composer.lock','package.json','package-lock.json']as$relative){$entry='active-code/'.$relative;$zip->addFile($root.'/'.$relative,$entry);$fileHashes[$entry]=hash_file('sha256',$root.'/'.$relative);}
|
|
$tlsBase='C:/Users/macbook/.config/investmoz/tls';
|
|
foreach(['portal-investidor.test-fullchain.pem','portal-investidor.test-key.pem']as$relative){$entry='tls/'.$relative;throw_unless(is_file($tlsBase.'/'.$relative),RuntimeException::class,'Current TLS recovery file missing.');$zip->addFile($tlsBase.'/'.$relative,$entry);$fileHashes[$entry]=hash_file('sha256',$tlsBase.'/'.$relative);}
|
|
$httpConfig=json_decode(file_get_contents($root.'/outputs/investmoz-country02-https-20261011/configuracao-ativa-leitura.json'),true,512,JSON_THROW_ON_ERROR);
|
|
$httpConfig['active_vhost']['path']='C:/laragon/etc/apache2/sites-enabled/portal-investidor.test.conf';
|
|
foreach(['active_vhost','apache_config','ssl_include']as$label){$file=$httpConfig[$label]['path'];$entry='apache/'.$label.'.conf';throw_unless(is_file($file),RuntimeException::class,'Current Apache recovery file missing.');$zip->addFile($file,$entry);$fileHashes[$entry]=hash_file('sha256',$file);}
|
|
foreach(glob('C:/laragon/etc/apache2/sites-enabled/*.conf')as$file){$entry='apache/vhosts/'.basename($file);$zip->addFile($file,$entry);$fileHashes[$entry]=hash_file('sha256',$file);}
|
|
"""+marker)
|
|
s=s.replace("'candidate_qa_manifest_sha256' => hash_file('sha256', $root.'/public/build-qa-opportunities/manifest.json')", "'candidate_qa_manifest_sha256' => $scope['production_manifest_sha256']")
|
|
start=s.index("$stagePath = ");end=s.index("file_put_contents($dir.'/backup-proof.json'",start)
|
|
s=s[:start]+"""$proof['result']=$missing===[]?'passed':'partial';
|
|
$proof['candidate_code_revision']=$hash;$proof['MFA_envelopes_readable']=$mfaReadable;
|
|
$proof['active_originals_backed_up']=count($documents);$proof['active_files_manifest']=$fileHashes;
|
|
$proof['scope']='Read-only backup of actual active target, restored exclusively to a new 55441 *_test clone; not installation or enablement';
|
|
$proof['activation_ready']=false;$proof['authenticated_installation_preview_still_required']=true;
|
|
"""+s[end:]
|
|
s=s.replace("/country-candidate-backup-latest.json", "/functional-scope-target-backup-link-20261011.json")
|
|
s=s.replace("echo json_encode(['result'", "file_put_contents($root.'/storage/app/private/investmoz-v2/portal-audit/functional-scope-target-backup-20261011.json',json_encode($proof,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));\necho json_encode(['result'")
|
|
assert 'OrganizationCountryInstallation' not in s and 'country_candidate_' not in s
|
|
(root/'tools/portal-functional-target-backup-prepare.php').write_text(s,encoding='utf8')
|
|
print('Prepared read-only target backup tool; no database or active configuration writes.')
|