mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-20 02:11:36 +00:00
2.x - console (wip)
This commit is contained in:
parent
906ddc38de
commit
63e7cfd8f7
@ -13,7 +13,7 @@
|
|||||||
* @method static init(Server $server, string $asUser = null)
|
* @method static init(Server $server, string $asUser = null)
|
||||||
* @method static setLog(?ServerLog $log)
|
* @method static setLog(?ServerLog $log)
|
||||||
* @method static connect()
|
* @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 assertExecuted(array|string $commands)
|
||||||
* @method static string assertExecutedContains(string $command)
|
* @method static string assertExecutedContains(string $command)
|
||||||
* @method static string assertFileUploaded(string $toPath, ?string $content = null)
|
* @method static string assertFileUploaded(string $toPath, ?string $content = null)
|
||||||
|
@ -91,7 +91,7 @@ public function connect(bool $sftp = false): void
|
|||||||
/**
|
/**
|
||||||
* @throws SSHError
|
* @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) {
|
if (! $this->log && $log) {
|
||||||
$this->log = ServerLog::make($this->server, $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);
|
$this->connection->setTimeout(0);
|
||||||
if ($stream) {
|
if ($stream) {
|
||||||
$this->connection->exec($command, function ($output) {
|
$this->connection->exec($command, function ($output) use ($streamCallback) {
|
||||||
$this->log?->write($output);
|
$this->log?->write($output);
|
||||||
echo $output;
|
|
||||||
ob_flush();
|
return $streamCallback($output);
|
||||||
flush();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
@ -97,8 +97,8 @@ public function panel(Panel $panel): Panel
|
|||||||
->authMiddleware([
|
->authMiddleware([
|
||||||
Authenticate::class,
|
Authenticate::class,
|
||||||
])
|
])
|
||||||
->login()
|
|
||||||
->spa()
|
->spa()
|
||||||
|
->login()
|
||||||
->globalSearchKeyBindings(['command+k', 'ctrl+k'])
|
->globalSearchKeyBindings(['command+k', 'ctrl+k'])
|
||||||
->sidebarCollapsibleOnDesktop()
|
->sidebarCollapsibleOnDesktop()
|
||||||
->globalSearchFieldKeyBindingSuffix();
|
->globalSearchFieldKeyBindingSuffix();
|
||||||
|
@ -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) {
|
if (! $this->log && $log) {
|
||||||
$this->log = $this->server->logs()->create([
|
$this->log = $this->server->logs()->create([
|
||||||
|
36
app/Web/Pages/Servers/Console/Index.php
Normal file
36
app/Web/Pages/Servers/Console/Index.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Web\Pages\Servers\Console;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Web\Components\Page;
|
||||||
|
use App\Web\Traits\PageHasServer;
|
||||||
|
|
||||||
|
class Index extends Page
|
||||||
|
{
|
||||||
|
use PageHasServer;
|
||||||
|
|
||||||
|
protected ?string $live = '';
|
||||||
|
|
||||||
|
protected $listeners = [];
|
||||||
|
|
||||||
|
protected static ?string $slug = 'servers/{server}/console';
|
||||||
|
|
||||||
|
protected static bool $shouldRegisterNavigation = false;
|
||||||
|
|
||||||
|
protected static ?string $title = 'Console';
|
||||||
|
|
||||||
|
public Server $server;
|
||||||
|
|
||||||
|
public static function canAccess(): bool
|
||||||
|
{
|
||||||
|
return auth()->user()?->can('update', static::getServerFromRoute()) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidgets(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[Widgets\Console::class, ['server' => $this->server]],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
98
app/Web/Pages/Servers/Console/Widgets/Console.php
Normal file
98
app/Web/Pages/Servers/Console/Widgets/Console.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Web\Pages\Servers\Console\Widgets;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Filament\Forms\Components\Actions\Action;
|
||||||
|
use Filament\Forms\Components\Grid;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Concerns\InteractsWithForms;
|
||||||
|
use Filament\Forms\Contracts\HasForms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class Console extends Component implements HasForms
|
||||||
|
{
|
||||||
|
use InteractsWithForms;
|
||||||
|
|
||||||
|
public Server $server;
|
||||||
|
|
||||||
|
protected $listeners = ['$refresh'];
|
||||||
|
|
||||||
|
public bool $running = false;
|
||||||
|
|
||||||
|
public string $output = '';
|
||||||
|
|
||||||
|
public ?array $data = [];
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->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'
|
||||||
|
<div>
|
||||||
|
<x-console-view wire:stream="output">
|
||||||
|
{{ $this->output }}
|
||||||
|
</x-console-view>
|
||||||
|
<div class="mt-5">
|
||||||
|
{{ $this->form }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
BLADE;
|
||||||
|
}
|
||||||
|
}
|
31
app/Web/Pages/Servers/Console/Widgets/StopCommand.php
Normal file
31
app/Web/Pages/Servers/Console/Widgets/StopCommand.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Web\Pages\Servers\Console\Widgets;
|
||||||
|
|
||||||
|
use Filament\Forms\Concerns\InteractsWithForms;
|
||||||
|
use Filament\Forms\Contracts\HasForms;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class StopCommand extends Component implements HasForms
|
||||||
|
{
|
||||||
|
use InteractsWithForms;
|
||||||
|
|
||||||
|
public function stop(): void
|
||||||
|
{
|
||||||
|
Cache::put('console', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): string
|
||||||
|
{
|
||||||
|
return <<<'BLADE'
|
||||||
|
<div>
|
||||||
|
<x-filament::icon
|
||||||
|
icon="heroicon-o-stop"
|
||||||
|
wire:click="stop"
|
||||||
|
class="h-5 w-5 text-danger-400 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
BLADE;
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Web\Traits;
|
namespace App\Web\Traits;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Web\Pages\Servers\Console\Index as ConsoleIndex;
|
||||||
use App\Web\Pages\Servers\CronJobs\Index as CronJobsIndex;
|
use App\Web\Pages\Servers\CronJobs\Index as CronJobsIndex;
|
||||||
use App\Web\Pages\Servers\Databases\Index as DatabasesIndex;
|
use App\Web\Pages\Servers\Databases\Index as DatabasesIndex;
|
||||||
use App\Web\Pages\Servers\Firewall\Index as FirewallIndex;
|
use App\Web\Pages\Servers\Firewall\Index as FirewallIndex;
|
||||||
@ -88,6 +89,13 @@ public function getSubNavigation(): array
|
|||||||
->url(MetricsIndex::getUrl(parameters: ['server' => $this->server]));
|
->url(MetricsIndex::getUrl(parameters: ['server' => $this->server]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ConsoleIndex::canAccess()) {
|
||||||
|
$items[] = NavigationItem::make(ConsoleIndex::getNavigationLabel())
|
||||||
|
->icon('heroicon-o-command-line')
|
||||||
|
->isActiveWhen(fn () => request()->routeIs(ConsoleIndex::getRouteName().'*'))
|
||||||
|
->url(ConsoleIndex::getUrl(parameters: ['server' => $this->server]));
|
||||||
|
}
|
||||||
|
|
||||||
if (LogsIndex::canAccess()) {
|
if (LogsIndex::canAccess()) {
|
||||||
$items[] = NavigationItem::make(LogsIndex::getNavigationLabel())
|
$items[] = NavigationItem::make(LogsIndex::getNavigationLabel())
|
||||||
->icon('heroicon-o-square-3-stack-3d')
|
->icon('heroicon-o-square-3-stack-3d')
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div
|
<div
|
||||||
{{ $attributes->merge(["class" => "font-mono whitespace-pre relative h-[500px] w-full overflow-auto whitespace-pre-line rounded-md border border-gray-200 bg-black p-5 text-gray-50 dark:border-gray-700"]) }}
|
{{ $attributes->merge(["class" => "font-mono whitespace-pre relative h-[500px] w-full overflow-auto whitespace-pre-line rounded-xl border border-gray-200 bg-black p-5 text-gray-50 dark:border-gray-800"]) }}
|
||||||
>
|
>
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user