resolveNewProjectScript($labRoot); $python = $this->resolvePythonBin(); $joined = implode(',', $domains); $cmd = [ $python, $script, '--channel-id', $channelId, '--deployment-domains', $joined, '--reporting-domains', $joined, ]; // cwd must stay inside open_basedir (server/). Script resolves LAB_ROOT from __file__. $process = new Process($cmd, base_path()); $process->setTimeout(600); $process->run(); if (! $process->isSuccessful()) { Log::error('new_project failed', [ 'channel_id' => $channelId, 'out' => $process->getOutput(), 'err' => $process->getErrorOutput(), ]); $this->deleteWebTree($channelId); throw new RuntimeException( '生成渠道资源失败: '.trim($process->getErrorOutput() ?: $process->getOutput() ?: 'unknown') ); } } public function deleteWebTree(string $channelId): void { $channelId = strtolower(trim($channelId)); if (! preg_match('/^[a-z0-9]{32}$/', $channelId)) { return; } $dir = public_path('web/'.$channelId); if (is_dir($dir)) { File::deleteDirectory($dir); } } private function resolveNewProjectScript(string $labRoot): string { $configured = trim((string) config('coruna.new_project_py', '')); if ($configured !== '') { return $configured; } return $labRoot.'/tools/new_project.py'; } /** * Prefer an absolute python path so the child process gets a non-empty sys.executable. */ private function resolvePythonBin(): string { $configured = trim((string) config('coruna.python_bin', 'python3')); if ($configured === '') { $configured = 'python3'; } if (str_contains($configured, '/')) { return $configured; } $which = trim((string) shell_exec('command -v '.escapeshellarg($configured).' 2>/dev/null')); if ($which !== '' && ! str_contains($which, "\n")) { return $which; } foreach (['/usr/bin/python3', '/usr/local/bin/python3'] as $fallback) { if (@is_file($fallback)) { return $fallback; } } return $configured; } }