diff --git a/app/Facades/SSH.php b/app/Facades/SSH.php index 74c0eaa..5cbb0a6 100644 --- a/app/Facades/SSH.php +++ b/app/Facades/SSH.php @@ -13,7 +13,7 @@ * @method static init(Server $server, string $asUser = null) * @method static setLog(?ServerLog $log) * @method static connect() - * @method static string exec(string $command, string $log = '', int $siteId = null, ?bool $stream = false) + * @method static string exec(string $command, string $log = '', int $siteId = null, ?bool $stream = false, callable $streamCallback = null) * @method static string assertExecuted(array|string $commands) * @method static string assertExecutedContains(string $command) * @method static string assertFileUploaded(string $toPath, ?string $content = null) diff --git a/app/Helpers/SSH.php b/app/Helpers/SSH.php index e281f0e..067c5dd 100755 --- a/app/Helpers/SSH.php +++ b/app/Helpers/SSH.php @@ -91,7 +91,7 @@ public function connect(bool $sftp = false): void /** * @throws SSHError */ - public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string + public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false, ?callable $streamCallback = null): string { if (! $this->log && $log) { $this->log = ServerLog::make($this->server, $log); @@ -116,11 +116,10 @@ public function exec(string $command, string $log = '', ?int $siteId = null, ?bo $this->connection->setTimeout(0); if ($stream) { - $this->connection->exec($command, function ($output) { + $this->connection->exec($command, function ($output) use ($streamCallback) { $this->log?->write($output); - echo $output; - ob_flush(); - flush(); + + return $streamCallback($output); }); return ''; diff --git a/app/Providers/WebServiceProvider.php b/app/Providers/WebServiceProvider.php index 5ae2629..5c6e7dd 100644 --- a/app/Providers/WebServiceProvider.php +++ b/app/Providers/WebServiceProvider.php @@ -97,8 +97,8 @@ public function panel(Panel $panel): Panel ->authMiddleware([ Authenticate::class, ]) - ->login() ->spa() + ->login() ->globalSearchKeyBindings(['command+k', 'ctrl+k']) ->sidebarCollapsibleOnDesktop() ->globalSearchFieldKeyBindingSuffix(); diff --git a/app/Support/Testing/SSHFake.php b/app/Support/Testing/SSHFake.php index 2551462..01c0711 100644 --- a/app/Support/Testing/SSHFake.php +++ b/app/Support/Testing/SSHFake.php @@ -40,7 +40,7 @@ public function connect(bool $sftp = false): void } } - public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false): string + public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false, ?callable $streamCallback = null): string { if (! $this->log && $log) { $this->log = $this->server->logs()->create([ diff --git a/app/Web/Pages/Servers/Console/Index.php b/app/Web/Pages/Servers/Console/Index.php new file mode 100644 index 0000000..b1bb5e2 --- /dev/null +++ b/app/Web/Pages/Servers/Console/Index.php @@ -0,0 +1,36 @@ +user()?->can('update', static::getServerFromRoute()) ?? false; + } + + public function getWidgets(): array + { + return [ + [Widgets\Console::class, ['server' => $this->server]], + ]; + } +} diff --git a/app/Web/Pages/Servers/Console/Widgets/Console.php b/app/Web/Pages/Servers/Console/Widgets/Console.php new file mode 100644 index 0000000..f9b1fc5 --- /dev/null +++ b/app/Web/Pages/Servers/Console/Widgets/Console.php @@ -0,0 +1,98 @@ +data['user'] = $this->server->ssh_user; + } + + public function form(Form $form): Form + { + return $form + ->statePath('data') + ->schema([ + Grid::make() + ->columns(6) + ->schema([ + Select::make('user') + ->options([ + 'root' => 'root', + 'vito' => 'vito', + ]) + ->maxWidth('sm') + ->hiddenLabel(), + TextInput::make('command') + ->hiddenLabel() + ->placeholder('Command') + ->columnSpan(5) + ->suffixActions( + [ + Action::make('run') + ->label('Run') + ->icon('heroicon-o-play') + ->color('primary') + ->visible(! $this->running) + ->action(function () { + $this->running = true; + $ssh = $this->server->ssh($this->data['user']); + $log = 'console-'.time(); + defer(function () use ($ssh, $log) { + $ssh->exec(command: $this->data['command'], log: $log, stream: true, streamCallback: function ($output) { + $this->output .= $output; + $this->stream( + to: 'output', + content: $output, + ); + }); + })->name($log); + }), + Action::make('stop') + ->view('web.components.dynamic-widget', [ + 'widget' => StopCommand::class, + 'params' => [], + ]), + ], + ), + ]), + ]); + } + + public function render(): string + { + return <<<'BLADE' +