isFile() && ! $file->isLink()) { $support[$directory.'/'.$file->getFilename()] = file_get_contents($file->getPathname()); } } } ksort($support); $support += $frontend['assets']; $support['scripts/sgf-internal-install.php'] = file_get_contents($root.'/scripts/sgf-internal-install.php'); $support['scripts/sgf-code-deploy.php'] = file_get_contents($root.'/scripts/sgf-code-deploy.php'); $support['bootstrap.php'] = file_get_contents($root.'/integrations/sgf/operational/entry.php'); $hash = hash('sha256', json_encode([$files, $support], JSON_THROW_ON_ERROR)); $directory = $root.'/storage/app/private/candidates/OPERATIONAL-CANDIDATE-'.substr($hash, 0, 16); $manifest = ['format' => 'tempo.operational-review.v1', 'installed' => false, 'activated' => false, 'host_version_preserved' => '0.1.1', 'host_overlays' => [], 'support_files' => []]; foreach ($files as $path => $file) { $support[$path] = $file['contents']; unset($file['contents']); $manifest['host_overlays'][$path] = $file; } foreach ($support as $path => $contents) { $target = $directory.'/'.$path; if (! is_dir(dirname($target))) { mkdir(dirname($target), 0700, true); } if (is_file($target) && hash_file('sha256', $target) !== hash('sha256', $contents)) { throw new RuntimeException('Candidata imutável divergente.'); } file_put_contents($target, $contents); $manifest['support_files'][$path] = hash('sha256', $contents); } file_put_contents($directory.'/manifest.json', json_encode($manifest, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); $archives = $root.'/storage/app/private/candidate-archives'; if (! is_dir($archives)) { mkdir($archives, 0700, true); } $archive = $archives.'/'.basename($directory).'.zip'; $creating = ! is_file($archive); $archivePath = $creating ? $archive.'.'.bin2hex(random_bytes(6)).'.tmp' : $archive; $zip = new ZipArchive; if ($zip->open($archivePath, $creating ? ZipArchive::CREATE | ZipArchive::EXCL : 0) !== true) { throw new RuntimeException('Arquivo de candidata inválido.'); } if ($creating) { foreach ([...array_keys($manifest['support_files']), 'manifest.json'] as $file) { $zip->addFile($directory.'/'.$file, $file); } if (! $zip->close()) { throw new RuntimeException('Falha ao preparar arquivo.'); } if ($zip->open($archivePath) !== true) { throw new RuntimeException('Arquivo incompleto.'); } } if ($zip->numFiles !== count($manifest['support_files']) + 1) { throw new RuntimeException('Conteúdo extra/ausente no arquivo.'); } foreach ($manifest['support_files'] + ['manifest.json' => hash_file('sha256', $directory.'/manifest.json')] as $file => $expected) { $size = filesize($directory.'/'.$file); $entry = $zip->statName($file); $body = $zip->getFromName($file, $size + 1); if (! $entry || $entry['size'] !== $size || $body === false || ! hash_equals($expected, hash('sha256', $body))) { throw new RuntimeException('Integridade do arquivo imutável divergente.'); } } $zip->close(); if ($creating && ! rename($archivePath, $archive)) { throw new RuntimeException('Falha ao concluir arquivo.'); } echo json_encode(['directory' => $directory, 'archive' => ['file' => $archive, 'sha256' => hash_file('sha256', $archive), 'manifest_sha256' => hash_file('sha256', $directory.'/manifest.json')], 'installed' => false], JSON_THROW_ON_ERROR)."\n";