65 lines
4.9 KiB
PHP
65 lines
4.9 KiB
PHP
<?php
|
|
|
|
// Rehearse recovery with the existing importer. Never accepts an active database.
|
|
require dirname(__DIR__).'/vendor/autoload.php';
|
|
$app = require dirname(__DIR__).'/bootstrap/app.php';
|
|
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
|
$target = Illuminate\Support\Facades\DB::selectOne('select current_database() as database,inet_server_port() as port');
|
|
throw_unless($target->database === 'simulator_fiscal_delta_20261021_test' && (int) $target->port === 55441
|
|
&& config('database.connections.pgsql.host') === '127.0.0.1', RuntimeException::class, 'Isolated QA only.');
|
|
$app->detectEnvironment(fn () => 'testing');
|
|
config(['geometry-import.enabled' => true, 'portal.managers' => ['qa-content-promoter@example.test'],
|
|
'services.malware.scanner' => 'clamd', 'services.malware.socket' => null,
|
|
'services.malware.host' => '127.0.0.1', 'services.malware.port' => 3310]);
|
|
$actor = App\Models\User::where('email', 'qa-content-promoter@example.test')->firstOrFail();
|
|
$directory = config('geometry-import.directory');
|
|
$out = storage_path('app/private/territory-demarcation-recovery');
|
|
Illuminate\Support\Facades\File::ensureDirectoryExists($out, 0700);
|
|
$manifest = [];
|
|
foreach (glob($directory.'/moz-adm*-ine-*') as $file) {
|
|
$manifest[] = ['file' => basename($file), 'bytes' => filesize($file), 'sha256' => hash_file('sha256', $file)];
|
|
}
|
|
file_put_contents($out.'/source-inventory.json', json_encode($manifest, JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
$scanner = app(App\Services\Malware\MalwareScanner::class);
|
|
$health = $scanner->health();
|
|
throw_unless($health['available'], RuntimeException::class, 'Current scanner unavailable.');
|
|
$scans = [];
|
|
foreach ([1, 2, 3] as $level) {
|
|
$file = $directory.'/moz-adm'.$level.'-ine-20190607.geojson';
|
|
// Scope the stream budget to this retained GIS original in this process only.
|
|
config(['services.malware.max_file_bytes' => filesize($file), 'services.malware.max_stream_bytes' => filesize($file)]);
|
|
$scan = $scanner->scan($file, filesize($file), storage_path('app/private'));
|
|
$scan['signatures_at'] = $scan['signatures_at']?->format(DATE_ATOM);
|
|
if ($level === 3 && $scan['reason_code'] === 'scanner_decompression_limit') {
|
|
$binary = getenv('GIS_CLAMSCAN_BINARY');
|
|
$database = getenv('GIS_CLAMSCAN_DATABASE');
|
|
throw_unless(is_file($binary ?: '') && is_dir($database ?: ''), RuntimeException::class, 'GIS scanner paths required.');
|
|
$process = new Symfony\Component\Process\Process([$binary, '--database='.$database, '--max-filesize=40M', '--max-scansize=100M', '--no-summary', $file]);
|
|
$process->setTimeout(120)->mustRun();
|
|
throw_unless(str_ends_with(trim($process->getOutput()), ': OK'), RuntimeException::class, 'GIS original scan incomplete.');
|
|
$version = new Symfony\Component\Process\Process([$binary, '--database='.$database, '--version']);
|
|
$version->mustRun();
|
|
$parts = explode('/', trim($version->getOutput()));
|
|
$signatureTime = strtotime(end($parts));
|
|
throw_unless($signatureTime && time() - $signatureTime < 48 * 3600, RuntimeException::class, 'Current GIS signatures required.');
|
|
$scan = ['result' => 'clean', 'engine' => 'ClamAV', 'engine_version' => trim($version->getOutput()),
|
|
'signatures_at' => gmdate(DATE_ATOM, $signatureTime), 'scope' => 'retained GIS original only',
|
|
'original_sha256' => hash_file('sha256', $file), 'stream_attempt' => $scan, 'exit_code' => $process->getExitCode()];
|
|
}
|
|
$scans[] = ['file' => basename($file), 'sha256' => hash_file('sha256', $file), 'scan' => $scan];
|
|
echo json_encode(['level' => $level, 'scan' => $scan]).PHP_EOL;
|
|
throw_unless($scan['result'] === 'clean', RuntimeException::class, 'Original scan incomplete.');
|
|
}
|
|
file_put_contents($directory.'/geometry-source-scans.json', json_encode(['captured_at' => gmdate('c'), 'files' => $scans], JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
$service = app(App\Services\TerritoryGeometryImport::class);
|
|
foreach ([1, 2, 3] as $level) {
|
|
$plan = $service->preview($actor, $level);
|
|
file_put_contents($out.'/preview-'.$level.'.json', json_encode($plan, JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo json_encode(['level' => $level, 'resolved' => $plan['resolved'], 'pending' => $plan['pending']]).PHP_EOL;
|
|
if (! in_array('--apply', $argv, true)) break; // Lower levels require imported parent correspondences.
|
|
$result = $service->apply($actor, ['level' => $level, 'hash' => $plan['hash'], 'idempotency_key' => (string) Illuminate\Support\Str::uuid(),
|
|
'reason' => 'Recuperação QA da demarcação anteriormente demonstrada, com originais e correspondências aceites.', 'confirm' => true]);
|
|
file_put_contents($out.'/result-'.$level.'.json', json_encode($result, JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo json_encode(['level' => $level, 'inserted' => $result['inserted'], 'reused' => $result['reused']]).PHP_EOL;
|
|
}
|