bootstrap($app); (new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app); $args = getopt('', ['manifest:', 'output:', 'apply', 'preview-sha256:', 'backup:', 'qa']); $manifestPath = $args['manifest'] ?? ''; $manifestHash = '8d07007a2e1e8758d4741b4ac594f78d5943f71ad78b2daae7f85aefa9d2de4a'; if (!is_file($manifestPath) || !hash_equals($manifestHash, hash_file('sha256', $manifestPath))) { throw new RuntimeException('Exact reviewed 29-original recovery manifest required.'); } $manifest = json_decode(file_get_contents($manifestPath), true, 512, JSON_THROW_ON_ERROR); if (count($manifest['items']) !== 29 || count(array_unique(array_column($manifest['items'], 'target_record_id'))) !== 29) { throw new RuntimeException('Exact original scope required.'); } $qa = isset($args['qa']); $c = config('database.connections.'.config('database.default')); $expected = $qa ? 'simulator_fiscal_delta_20261021_test' : 'teste_investidor'; $port = $qa ? 55441 : 5432; if ($c['driver'] !== 'pgsql' || !empty($c['url']) || !in_array($c['host'], ['127.0.0.1', 'localhost'], true) || $c['database'] !== $expected || (int)$c['port'] !== $port) { throw new RuntimeException('Named target mismatch.'); } if (!$qa && (!getenv('SSH_CONNECTION') || !function_exists('posix_geteuid') || posix_geteuid() !== 0 || rtrim(config('app.url'), '/') !== 'https://teste-investidor.tempo.co.mz')) { throw new RuntimeException('Authenticated destination operator required.'); } $output = $args['output'] ?? ''; if (!$output || str_contains(str_replace('\\', '/', $output), '/public/')) { throw new RuntimeException('Private receipt path required.'); } $pdo = new PDO('pgsql:host='.$c['host'].';port='.$c['port'].';dbname='.$expected, $c['username'], $c['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); $apply = isset($args['apply']); $pdo->exec($apply ? 'BEGIN ISOLATION LEVEL SERIALIZABLE' : 'BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY'); try { $actual = $pdo->query('select current_database() as name,inet_server_port() as port')->fetch(PDO::FETCH_ASSOC); if ($actual['name'] !== $expected || (int)$actual['port'] !== $port) throw new RuntimeException('Actual target mismatch.'); if ($apply) $pdo->query('select pg_advisory_xact_lock(231026,29)'); $plan = []; $before = []; foreach ($manifest['items'] as $item) { $query = $pdo->prepare('select * from investment_dossiers where id = ?'.($apply ? ' for update' : '')); $query->execute([$item['target_record_id']]); $row = $query->fetch(PDO::FETCH_ASSOC); if (!$row || $row['deleted_at'] !== null || $row['title'] !== $item['title'] || !hash_equals($item['content_sha256'], hash('sha256', $row['content'])) || !hash_equals($item['source_sha256'], hash('sha256', $row['source']))) { throw new RuntimeException('Missing, archived or changed original: '.$item['code']); } if (!hash_equals($item['approval_sha256'], hash('sha256', $item['approval_evidence']))) throw new RuntimeException('Approval provenance changed.'); $replayed = $pdo->prepare("select count(*) from audit_events where source_system='portfolio-visibility-recovery-v1' and external_id=? and entity_id=? and action='approved_visibility_restored'"); $replayed->execute([$manifestHash.':'.$row['id'], $row['id']]); $already = (int)$replayed->fetchColumn() === 1; if ($already) { if ($row['publication_status'] !== 'published' || $row['approval_basis'] !== 'document_owner' || $row['approval_evidence'] !== $item['approval_evidence']) throw new RuntimeException('Recovery subsequently changed: '.$item['code']); } elseif ($row['publication_status'] !== 'draft' || $row['editor_id'] !== null || $row['reviewer_id'] !== null || (int)$row['version_no'] !== (int)$item['expected_target_version']) { throw new RuntimeException('Preserve subsequent editorial decisions: '.$item['code']); } $before[] = $row; $plan[] = ['id' => $row['id'], 'code' => $item['code'], 'version' => (int)$row['version_no'], 'status' => $row['publication_status'], 'content_sha256' => $item['content_sha256'], 'source_sha256' => $item['source_sha256'], 'already_restored' => $already]; } $previewHash = hash('sha256', json_encode([$expected, $manifestHash, $plan], JSON_THROW_ON_ERROR)); if ($apply) { if (!hash_equals($previewHash, $args['preview-sha256'] ?? '')) throw new RuntimeException('Preview changed; recover current state before writing.'); if (!$qa) { $directory = $args['backup'] ?? ''; $backup = json_decode(file_get_contents($directory.'/backup-receipt.json'), true, 512, JSON_THROW_ON_ERROR); $restore = json_decode(file_get_contents($directory.'/restore-receipt.json'), true, 512, JSON_THROW_ON_ERROR); if (!$restore['restore_verified'] || $backup['target']['database'] !== $expected || (int)$backup['target']['port'] !== $port || !hash_equals($backup['sha256'], $restore['source_dump_sha256'])) throw new RuntimeException('Verified matching backup/restore required.'); } $snapshot = $output.'.before.json'; if (is_file($snapshot)) throw new RuntimeException('Preserve earlier recovery snapshot; use a new receipt path.'); file_put_contents($snapshot, json_encode(['database' => $expected, 'rows' => $before], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); if (!$qa) chmod($snapshot, 0600); foreach ($plan as $index => $entry) { if ($entry['already_restored']) continue; $item = $manifest['items'][$index]; $update = $pdo->prepare("update investment_dossiers set publication_status='published',approval_basis='document_owner',approval_evidence=?,published_at=now(),version_no=version_no+1,updated_at=now() where id=? and version_no=?"); $update->execute([$item['approval_evidence'], $entry['id'], $entry['version']]); if ($update->rowCount() !== 1) throw new RuntimeException('Concurrent change.'); $event = $pdo->prepare("insert into audit_events(source_system,external_id,entity_type,entity_id,actor_user_id,action,before_data,after_data) values('portfolio-visibility-recovery-v1',?,'investment_dossiers',?,NULL,'approved_visibility_restored',?,?)"); $event->execute([$manifestHash.':'.$entry['id'], $entry['id'], json_encode(['version_no' => $entry['version'], 'publication_status' => 'draft', 'approval_basis' => $before[$index]['approval_basis']], JSON_THROW_ON_ERROR), json_encode(['code' => $item['code'], 'version_no' => $entry['version']+1, 'publication_status' => 'published', 'approval_basis' => 'document_owner', 'approval_sha256' => $item['approval_sha256'], 'content_sha256' => $item['content_sha256'], 'source_record_id' => $item['source_record_id'], 'source_published_at' => $item['original_published_at'], 'instruction' => 'Tempo 23/10/2026: preserve and restore completed approved functionality', 'authenticated_transport' => $qa ? 'isolated-QA' : 'SSH', 'not_a_new_editorial_approval' => true], JSON_THROW_ON_ERROR)]); } } $visible = (int)$pdo->query("select count(*) from investment_dossiers d where publication_status='published' and (exists(select 1 from projects p where p.id=d.project_id and p.deleted_at is null) or exists(select 1 from economic_clusters c where c.id=d.cluster_id and c.deleted_at is null) or exists(select 1 from development_corridors r where r.id=d.corridor_id and r.deleted_at is null))")->fetchColumn(); $result = ['database' => $actual, 'qa' => $qa, 'mode' => $apply ? 'applied' : 'preview', 'manifest_sha256' => $manifestHash, 'preview_sha256' => $previewHash, 'scope' => count($plan), 'restored' => $apply ? count(array_filter($plan, fn($r) => !$r['already_restored'])) : 0, 'visible' => $visible, 'data_content_changed' => false, 'new_dossiers_imported' => 0, 'fake_editor_or_reviewer_assigned' => false, 'plan' => $plan]; if ($apply) $pdo->commit(); else $pdo->rollBack(); file_put_contents($output, json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); if (!$qa) chmod($output, 0600); unset($result['plan']); echo json_encode($result, JSON_THROW_ON_ERROR); } catch (Throwable $error) { if ($pdo->inTransaction()) $pdo->rollBack(); throw $error; }