75 lines
3.9 KiB
PHP
75 lines
3.9 KiB
PHP
<?php
|
|
|
|
use App\Services\VirtualDeskInstallation;
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
$root = dirname(__DIR__);
|
|
$proof = json_decode(file_get_contents($root.'/storage/app/private/virtual-desk/backup-proof.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
$target = $proof['restored_target'];
|
|
if (! preg_match('/^desk_restore_\d{8}_\d{6}_test$/', $target['database']) || (int) $target['port'] !== 55441) {
|
|
throw new RuntimeException('Isolated restore required.');
|
|
}
|
|
foreach (['APP_ENV' => 'testing', 'DB_CONNECTION' => 'pgsql', 'DB_HOST' => '127.0.0.1', 'DB_PORT' => '55441',
|
|
'DB_DATABASE' => $target['database'], 'DB_USERNAME' => 'postgres', 'DB_PASSWORD' => '', 'DB_URL' => '',
|
|
'CACHE_STORE' => 'array', 'SESSION_DRIVER' => 'array', 'QUEUE_CONNECTION' => 'sync'] as $key => $value) {
|
|
putenv($key.'='.$value);
|
|
$_ENV[$key] = $_SERVER[$key] = $value;
|
|
}
|
|
require $root.'/vendor/autoload.php';
|
|
$app = require $root.'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
$db = DB::connection();
|
|
$identity = $db->selectOne('select current_database() as database, inet_server_port() as port');
|
|
if ($identity->database !== $target['database'] || (int) $identity->port !== 55441) {
|
|
throw new RuntimeException('Actual QA identity rejected.');
|
|
}
|
|
$check = function (bool $extended) use ($db, $proof) {
|
|
foreach ($proof['table_hashes'] as $name => $hash) {
|
|
// Extension adds nullable columns to existing tasks; compare original columns explicitly.
|
|
$columns = '*';
|
|
if ($extended && $name === '"public"."tasks"') {
|
|
$columns = implode(',', array_map(fn ($r) => '"'.$r->column_name.'"', $db->select("select column_name from information_schema.columns where table_schema='public' and table_name='tasks' and column_name not in ('desk_case_id','desk_request_id','guidance_key','accepted_by','accepted_at','external_sync_status') order by ordinal_position")));
|
|
}
|
|
$rows = array_map(fn ($r) => $r->value, $db->select('select row_to_json(t)::text as value from (select '.$columns.' from '.$name.') t order by row_to_json(t)::text'));
|
|
if (hash('sha256', implode("\n", $rows)) !== $hash) {
|
|
throw new RuntimeException('Original contents changed: '.$name);
|
|
}
|
|
}
|
|
};
|
|
$check(false);
|
|
$migrations = array_map(fn ($name) => require $root.'/database/migrations/'.$name.'.php', VirtualDeskInstallation::MIGRATIONS);
|
|
$db->transaction(function () use ($migrations, $check) {
|
|
foreach ($migrations as $migration) {
|
|
$migration->up();
|
|
}
|
|
$check(true);
|
|
foreach (array_reverse($migrations) as $migration) {
|
|
$migration->down();
|
|
}
|
|
$check(false);
|
|
});
|
|
$archive = new ZipArchive;
|
|
$archive->open($proof['files_path']);
|
|
$restoreRoot = dirname($proof['files_path']).'/restored-files';
|
|
foreach ($proof['file_hashes'] as $name => $hash) {
|
|
if (str_contains($name, '..') || str_starts_with($name, '/')) {
|
|
throw new RuntimeException('Invalid archive entry.');
|
|
}
|
|
$path = $restoreRoot.'/'.$name;
|
|
if (! is_dir(dirname($path))) {
|
|
mkdir(dirname($path), 0700, true);
|
|
}
|
|
file_put_contents($path, $archive->getFromName($name));
|
|
if (hash_file('sha256', $path) !== $hash) {
|
|
throw new RuntimeException('Restored private bytes differ.');
|
|
}
|
|
}
|
|
$archive->close();
|
|
$result = ['passed' => true, 'verified_at' => gmdate('c'), 'database' => $target['database'], 'port' => 55441,
|
|
'migrations' => VirtualDeskInstallation::MIGRATIONS, 'original_tables_preserved' => count($proof['table_hashes']),
|
|
'empty_rollback_verified' => true, 'private_files_restored' => count($proof['file_hashes']),
|
|
'dump_sha256' => $proof['sha256'], 'code_hash' => app(VirtualDeskInstallation::class)->fingerprint()];
|
|
file_put_contents($root.'/storage/app/private/virtual-desk/activation-compatibility.json', json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
|
echo json_encode($result, JSON_THROW_ON_ERROR);
|