From 06d690138d6a41bf05e2edad2f2b6095bf87986b Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Sun, 6 Oct 2024 21:16:58 +0200 Subject: [PATCH] - 2.x - console --- app/Http/Controllers/ConsoleController.php | 18 +- .../Pages/Servers/Console/Widgets/Console.php | 89 +--------- .../Pages/Servers/Widgets/ServerDetails.php | 7 +- .../views/web/components/console.blade.php | 103 +++++++++++ routes/server.php | 163 ------------------ routes/settings.php | 75 -------- routes/web.php | 41 +---- 7 files changed, 124 insertions(+), 372 deletions(-) create mode 100644 resources/views/web/components/console.blade.php delete mode 100644 routes/server.php delete mode 100644 routes/settings.php diff --git a/app/Http/Controllers/ConsoleController.php b/app/Http/Controllers/ConsoleController.php index 6905858..4812d8c 100644 --- a/app/Http/Controllers/ConsoleController.php +++ b/app/Http/Controllers/ConsoleController.php @@ -3,24 +3,14 @@ namespace App\Http\Controllers; use App\Models\Server; -use Illuminate\Contracts\View\View; use Illuminate\Http\Request; use Illuminate\Validation\Rule; class ConsoleController extends Controller { - public function index(Server $server): View - { - $this->authorize('manage', $server); - - return view('console.index', [ - 'server' => $server, - ]); - } - public function run(Server $server, Request $request) { - $this->authorize('manage', $server); + $this->authorize('update', $server); $this->validate($request, [ 'user' => [ @@ -34,7 +24,11 @@ public function run(Server $server, Request $request) function () use ($server, $request) { $ssh = $server->ssh($request->user); $log = 'console-'.time(); - $ssh->exec(command: $request->command, log: $log, stream: true); + $ssh->exec(command: $request->command, log: $log, stream: true, streamCallback: function ($output) { + echo $output; + ob_flush(); + flush(); + }); }, 200, [ diff --git a/app/Web/Pages/Servers/Console/Widgets/Console.php b/app/Web/Pages/Servers/Console/Widgets/Console.php index cc35efc..7e4d2cf 100644 --- a/app/Web/Pages/Servers/Console/Widgets/Console.php +++ b/app/Web/Pages/Servers/Console/Widgets/Console.php @@ -3,94 +3,17 @@ 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 Illuminate\Contracts\View\View; use Livewire\Component; -class Console extends Component implements HasForms +class Console extends Component { - use InteractsWithForms; - public Server $server; - protected $listeners = ['$refresh']; - - public bool $running = false; - - public string $output = ''; - - public ?array $data = []; - - public function mount(): void + public function render(): View { - $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(); - $ssh->exec(command: $this->data['command'], log: $log, stream: true, streamCallback: function ($output) { - $this->output .= $output; - $this->stream( - to: 'output', - content: $output, - ); - }); - }), - Action::make('stop') - ->view('web.components.dynamic-widget', [ - 'widget' => StopCommand::class, - 'params' => [], - ]), - ], - ), - ]), - ]); - } - - public function render(): string - { - return <<<'BLADE' -
- - {{ $this->output }} - -
- {{ $this->form }} -
-
- BLADE; + return view('web.components.console', [ + 'server' => $this->server, + ]); } } diff --git a/app/Web/Pages/Servers/Widgets/ServerDetails.php b/app/Web/Pages/Servers/Widgets/ServerDetails.php index 25ade22..52cb96a 100644 --- a/app/Web/Pages/Servers/Widgets/ServerDetails.php +++ b/app/Web/Pages/Servers/Widgets/ServerDetails.php @@ -99,7 +99,12 @@ public function infolist(Infolist $infolist): Infolist ->suffixAction( EditTags::infolist($this->server) ), - + TextEntry::make('public_key') + ->label('Public Key') + ->limit(30) + ->inlineLabel() + ->copyable() + ->tooltip('Copy public key to clipboard'), ]), ]) ->record($this->server); diff --git a/resources/views/web/components/console.blade.php b/resources/views/web/components/console.blade.php new file mode 100644 index 0000000..6795a69 --- /dev/null +++ b/resources/views/web/components/console.blade.php @@ -0,0 +1,103 @@ +
$server]) }}', + async run() { + if (! this.command) return + this.running = true + this.output = this.command + '\n' + const fetchOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': '{{ csrf_token() }}', + }, + body: JSON.stringify({ + user: this.user, + command: this.command, + }), + } + + this.command = '' + const response = await fetch(this.runUrl, fetchOptions) + const reader = response.body.getReader() + const decoder = new TextDecoder('utf-8') + + while (true) { + if (! this.running) { + reader.cancel() + this.output += '\nStopped!' + break + } + const { value, done } = await reader.read() + if (done) break + + const textChunk = decoder.decode(value, { stream: true }) + + this.output += textChunk + + document.getElementById('console-output').scrollTop = + document.getElementById('console-output').scrollHeight + } + this.output += '\nDone!' + this.running = false + }, + stop() { + this.running = false + }, + }" +> +
+ +
+
+
+
+ + + + + + + + + + + +
+
+
+
diff --git a/routes/server.php b/routes/server.php deleted file mode 100644 index fbecbd5..0000000 --- a/routes/server.php +++ /dev/null @@ -1,163 +0,0 @@ -name('servers'); -Route::get('/create', [ServerController::class, 'create'])->name('servers.create'); -Route::post('/create', [ServerController::class, 'store']); - -Route::middleware('select-current-project')->group(function () { - Route::get('/{server}', [ServerController::class, 'show'])->name('servers.show'); - Route::delete('/{server}', [ServerController::class, 'delete'])->name('servers.delete'); - - Route::middleware(['server-is-ready', 'handle-ssh-errors'])->group(function () { - Route::prefix('/{server}/sites')->group(function () { - // sites - Route::get('/', [SiteController::class, 'index'])->name('servers.sites'); - Route::get('/create', [SiteController::class, 'create'])->name('servers.sites.create'); - Route::post('/create', [SiteController::class, 'store']); - Route::get('/{site}', [SiteController::class, 'show'])->name('servers.sites.show'); - Route::delete('/{site}', [SiteController::class, 'destroy'])->name('servers.sites.destroy'); - Route::get('/{site}/installing', [SiteController::class, 'installing'])->name('servers.sites.installing'); - - // site application - Route::post('/{site}/application/deploy', [ApplicationController::class, 'deploy'])->name('servers.sites.application.deploy'); - Route::get('/{site}/application/{deployment}/log', [ApplicationController::class, 'showDeploymentLog'])->name('servers.sites.application.deployment.log'); - Route::post('/{site}/application/deployment-script', [ApplicationController::class, 'updateDeploymentScript'])->name('servers.sites.application.deployment-script'); - Route::post('/{site}/application/branch', [ApplicationController::class, 'updateBranch'])->name('servers.sites.application.branch'); - Route::get('/{site}/application/env', [ApplicationController::class, 'getEnv'])->name('servers.sites.application.env'); - Route::post('/{site}/application/env', [ApplicationController::class, 'updateEnv']); - Route::post('/{site}/application/auto-deployment', [ApplicationController::class, 'enableAutoDeployment'])->name('servers.sites.application.auto-deployment'); - Route::delete('/{site}/application/auto-deployment', [ApplicationController::class, 'disableAutoDeployment']); - - // site ssl - Route::get('/{site}/ssl', [SSLController::class, 'index'])->name('servers.sites.ssl'); - Route::post('/{site}/ssl', [SSLController::class, 'store'])->name('servers.sites.ssl.store'); - Route::delete('/{site}/ssl/{ssl}', [SSLController::class, 'destroy'])->name('servers.sites.ssl.destroy'); - - // site queues - Route::get('/{site}/queues', [QueueController::class, 'index'])->name('servers.sites.queues'); - Route::post('/{site}/queues', [QueueController::class, 'store'])->name('servers.sites.queues.store'); - Route::post('/{site}/queues/{queue}/action/{action}', [QueueController::class, 'action'])->name('servers.sites.queues.action'); - Route::delete('/{site}/queues/{queue}', [QueueController::class, 'destroy'])->name('servers.sites.queues.destroy'); - Route::get('/{site}/queues/{queue}/logs', [QueueController::class, 'logs'])->name('servers.sites.queues.logs'); - - // site settings - Route::get('/{site}/settings', [SiteSettingController::class, 'index'])->name('servers.sites.settings'); - Route::get('/{site}/settings/vhost', [SiteSettingController::class, 'getVhost'])->name('servers.sites.settings.vhost'); - Route::post('/{site}/settings/vhost', [SiteSettingController::class, 'updateVhost']); - Route::post('/{site}/settings/php', [SiteSettingController::class, 'updatePHPVersion'])->name('servers.sites.settings.php'); - Route::post('/{site}/settings/source-control', [SiteSettingController::class, 'updateSourceControl'])->name('servers.sites.settings.source-control'); - Route::post('/{site}/settings/update-aliases', [SiteSettingController::class, 'updateAliases'])->name('servers.sites.settings.aliases'); - - // site logs - Route::get('/{site}/logs', [SiteLogController::class, 'index'])->name('servers.sites.logs'); - }); - - Route::prefix('/{server}/databases')->group(function () { - // databases - Route::get('/', [DatabaseController::class, 'index'])->name('servers.databases'); - Route::post('/', [DatabaseController::class, 'store'])->name('servers.databases.store'); - Route::delete('/{database}', [DatabaseController::class, 'destroy'])->name('servers.databases.destroy'); - - // database users - Route::post('/users', [DatabaseUserController::class, 'store'])->name('servers.databases.users.store'); - Route::delete('/users/{databaseUser}', [DatabaseUserController::class, 'destroy'])->name('servers.databases.users.destroy'); - Route::post('/users/{databaseUser}/password', [DatabaseUserController::class, 'password'])->name('servers.databases.users.password'); - Route::post('/users/{databaseUser}/link', [DatabaseUserController::class, 'link'])->name('servers.databases.users.link'); - - // database backups - Route::post('/backups', [DatabaseBackupController::class, 'store'])->name('servers.databases.backups.store'); - Route::delete('/backups/{backup}', [DatabaseBackupController::class, 'destroy'])->name('servers.databases.backups.destroy'); - - // database backup files - Route::get('/backups/{backup}', [DatabaseBackupController::class, 'show'])->name('servers.databases.backups'); - Route::get('/backups/{backup}/run', [DatabaseBackupController::class, 'run'])->name('servers.databases.backups.run'); - Route::post('/backups/{backup}/files/{backupFile}/restore', [DatabaseBackupController::class, 'restore'])->name('servers.databases.backups.files.restore'); - Route::delete('/backups/{backup}/files/{backupFile}', [DatabaseBackupController::class, 'destroyFile'])->name('servers.databases.backups.files.destroy'); - }); - - // php - Route::get('/{server}/php', [PHPController::class, 'index'])->name('servers.php'); - Route::post('/{server}/php/install', [PHPController::class, 'install'])->name('servers.php.install'); - Route::post('/{server}/php/install-extension', [PHPController::class, 'installExtension'])->name('servers.php.install-extension'); - Route::post('/{server}/php/default-cli', [PHPController::class, 'defaultCli'])->name('servers.php.default-cli'); - Route::get('/{server}/php/get-ini', [PHPController::class, 'getIni'])->name('servers.php.get-ini'); - Route::post('/{server}/php/update-ini', [PHPController::class, 'updateIni'])->name('servers.php.update-ini'); - Route::delete('/{server}/php/uninstall', [PHPController::class, 'uninstall'])->name('servers.php.uninstall'); - - // firewall - Route::get('/{server}/firewall', [FirewallController::class, 'index'])->name('servers.firewall'); - Route::post('/{server}/firewall', [FirewallController::class, 'store'])->name('servers.firewall.store'); - Route::delete('/{server}/firewall/{firewallRule}', [FirewallController::class, 'destroy'])->name('servers.firewall.destroy'); - - // cronjobs - Route::get('/{server}/cronjobs', [CronjobController::class, 'index'])->name('servers.cronjobs'); - Route::post('/{server}/cronjobs', [CronjobController::class, 'store'])->name('servers.cronjobs.store'); - Route::delete('/{server}/cronjobs/{cronJob}', [CronjobController::class, 'destroy'])->name('servers.cronjobs.destroy'); - Route::post('/{server}/cronjobs/{cronJob}/enable', [CronjobController::class, 'enable'])->name('servers.cronjobs.enable'); - Route::post('/{server}/cronjobs/{cronJob}/disable', [CronjobController::class, 'disable'])->name('servers.cronjobs.disable'); - - // ssh keys - Route::get('/{server}/ssh-keys', [SSHKeyController::class, 'index'])->name('servers.ssh-keys'); - Route::post('/{server}/ssh-keys', [SSHKeyController::class, 'store'])->name('servers.ssh-keys.store'); - Route::delete('/{server}/ssh-keys/{sshKey}', [SSHKeyController::class, 'destroy'])->name('servers.ssh-keys.destroy'); - Route::post('/{server}/ssh-keys/deploy', [SSHKeyController::class, 'deploy'])->name('servers.ssh-keys.deploy'); - - // services - Route::get('/{server}/services', [ServiceController::class, 'index'])->name('servers.services'); - Route::get('/{server}/services/{service}/start', [ServiceController::class, 'start'])->name('servers.services.start'); - Route::get('/{server}/services/{service}/stop', [ServiceController::class, 'stop'])->name('servers.services.stop'); - Route::get('/{server}/services/{service}/restart', [ServiceController::class, 'restart'])->name('servers.services.restart'); - Route::get('/{server}/services/{service}/enable', [ServiceController::class, 'enable'])->name('servers.services.enable'); - Route::get('/{server}/services/{service}/disable', [ServiceController::class, 'disable'])->name('servers.services.disable'); - Route::post('/{server}/services/install', [ServiceController::class, 'install'])->name('servers.services.install'); - Route::delete('/{server}/services/{service}/uninstall', [ServiceController::class, 'uninstall'])->name('servers.services.uninstall'); - - // metrics - Route::get('/{server}/metrics', [MetricController::class, 'index'])->name('servers.metrics'); - Route::post('/{server}/metrics/settings', [MetricController::class, 'settings'])->name('servers.metrics.settings'); - - // console - Route::get('/{server}/console', [ConsoleController::class, 'index'])->name('servers.console'); - Route::post('/{server}/console', [ConsoleController::class, 'run'])->name('servers.console.run'); - }); - - // settings - Route::prefix('/{server}/settings')->group(function () { - Route::get('/', [ServerSettingController::class, 'index'])->name('servers.settings'); - Route::post('/check-connection', [ServerSettingController::class, 'checkConnection'])->name('servers.settings.check-connection'); - Route::post('/reboot', [ServerSettingController::class, 'reboot'])->name('servers.settings.reboot'); - Route::post('/edit', [ServerSettingController::class, 'edit'])->name('servers.settings.edit'); - Route::post('/check-updates', [ServerSettingController::class, 'checkUpdates'])->name('servers.settings.check-updates'); - Route::post('/update', [ServerSettingController::class, 'update'])->name('servers.settings.update'); - }); - - // logs - Route::prefix('/{server}/logs')->group(function () { - Route::get('/', [ServerLogController::class, 'index'])->name('servers.logs'); - Route::get('/remote', [ServerLogController::class, 'remote'])->name('servers.logs.remote'); - Route::post('/remote', [ServerLogController::class, 'store'])->name('servers.logs.remote.store'); - Route::delete('/remote/{serverLog}', [ServerLogController::class, 'destroy'])->name('servers.logs.remote.destroy'); - Route::get('/{serverLog}', [ServerLogController::class, 'show'])->name('servers.logs.show'); - }); -}); diff --git a/routes/settings.php b/routes/settings.php deleted file mode 100644 index ea9adb4..0000000 --- a/routes/settings.php +++ /dev/null @@ -1,75 +0,0 @@ -group(function () { - Route::get('/', [UserController::class, 'index'])->name('settings.users.index'); - Route::post('/', [UserController::class, 'store'])->name('settings.users.store'); - Route::get('/{user}', [UserController::class, 'show'])->name('settings.users.show'); - Route::post('/{user}', [UserController::class, 'update'])->name('settings.users.update'); - Route::post('/{user}/projects', [UserController::class, 'updateProjects'])->name('settings.users.update-projects'); - Route::delete('/{user}', [UserController::class, 'destroy'])->name('settings.users.delete'); -}); - -// projects -Route::prefix('settings/projects')->group(function () { - Route::get('/', [ProjectController::class, 'index'])->name('settings.projects'); - Route::post('create', [ProjectController::class, 'create'])->name('settings.projects.create'); - Route::post('update/{project}', [ProjectController::class, 'update'])->name('settings.projects.update'); - Route::delete('delete/{project}', [ProjectController::class, 'delete'])->name('settings.projects.delete'); -}); - -// server-providers -Route::prefix('settings/server-providers')->group(function () { - Route::get('/', [ServerProviderController::class, 'index'])->name('settings.server-providers'); - Route::post('connect', [ServerProviderController::class, 'connect'])->name('settings.server-providers.connect'); - Route::delete('delete/{serverProvider}', [ServerProviderController::class, 'delete'])->name('settings.server-providers.delete'); - Route::post('edit/{serverProvider}', [ServerProviderController::class, 'update'])->name('settings.server-providers.update'); -}); - -// source-controls -Route::prefix('settings/source-controls')->group(function () { - Route::get('/', [SourceControlController::class, 'index'])->name('settings.source-controls'); - Route::post('connect', [SourceControlController::class, 'connect'])->name('settings.source-controls.connect'); - Route::delete('delete/{sourceControl}', [SourceControlController::class, 'delete'])->name('settings.source-controls.delete'); - Route::post('edit/{sourceControl}', [SourceControlController::class, 'update'])->name('settings.source-controls.update'); -}); - -// storage-providers -Route::prefix('settings/storage-providers')->group(function () { - Route::get('/', [StorageProviderController::class, 'index'])->name('settings.storage-providers'); - Route::post('connect', [StorageProviderController::class, 'connect'])->name('settings.storage-providers.connect'); - Route::delete('delete/{storageProvider}', [StorageProviderController::class, 'delete'])->name('settings.storage-providers.delete'); - Route::post('edit/{storageProvider}', [StorageProviderController::class, 'update'])->name('settings.storage-providers.update'); -}); - -// notification-channels -Route::prefix('settings/notification-channels')->group(function () { - Route::get('/', [NotificationChannelController::class, 'index'])->name('settings.notification-channels'); - Route::post('add', [NotificationChannelController::class, 'add'])->name('settings.notification-channels.add'); - Route::delete('delete/{id}', [NotificationChannelController::class, 'delete'])->name('settings.notification-channels.delete'); - Route::post('edit/{notificationChannel}', [NotificationChannelController::class, 'update'])->name('settings.notification-channels.update'); -}); - -// ssh-keys -Route::prefix('settings/ssh-keys')->group(function () { - Route::get('/', [SSHKeyController::class, 'index'])->name('settings.ssh-keys'); - Route::post('add', [SshKeyController::class, 'add'])->name('settings.ssh-keys.add'); - Route::delete('delete/{id}', [SshKeyController::class, 'delete'])->name('settings.ssh-keys.delete'); -}); - -// tags -Route::prefix('/tags')->group(function () { - Route::get('/', [TagController::class, 'index'])->name('settings.tags'); - Route::post('/create', [TagController::class, 'create'])->name('settings.tags.create'); - Route::post('/{tag}', [TagController::class, 'update'])->name('settings.tags.update'); - Route::delete('/{tag}', [TagController::class, 'delete'])->name('settings.tags.delete'); -}); diff --git a/routes/web.php b/routes/web.php index d3e5f8f..7ff3e19 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,10 +1,6 @@ group(function () { - // profile - Route::prefix('profile')->group(function () { - Route::get('/', [ProfileController::class, 'index'])->name('profile'); - Route::post('info', [ProfileController::class, 'info'])->name('profile.info'); - Route::post('password', [ProfileController::class, 'password'])->name('profile.password'); + Route::middleware(['server-is-ready'])->group(function () { + Route::post('/{server}/console', [ConsoleController::class, 'run'])->name('servers.console.run'); }); - - // switch project - Route::get('settings/projects/switch/{project}', [ProjectController::class, 'switch'])->name('settings.projects.switch'); - - Route::middleware('is-admin')->group(function () { - require __DIR__.'/settings.php'; - }); - - Route::prefix('/settings/tags')->group(function () { - Route::post('/attach', [TagController::class, 'attach'])->name('tags.attach'); - Route::post('/{tag}/detach', [TagController::class, 'detach'])->name('tags.detach'); - }); - - Route::prefix('/servers')->middleware('must-have-current-project')->group(function () { - require __DIR__.'/server.php'; - }); - - Route::prefix('/scripts')->group(function () { - Route::get('/', [ScriptController::class, 'index'])->name('scripts.index'); - Route::post('/', [ScriptController::class, 'store'])->name('scripts.store'); - Route::get('/{script}', [ScriptController::class, 'show'])->name('scripts.show'); - Route::post('/{script}/edit', [ScriptController::class, 'edit'])->name('scripts.edit'); - Route::post('/{script}/execute', [ScriptController::class, 'execute'])->name('scripts.execute'); - Route::delete('/{script}/delete', [ScriptController::class, 'delete'])->name('scripts.delete'); - Route::get('/{script}/log/{execution}', [ScriptController::class, 'log'])->name('scripts.log'); - }); - - Route::get('/search', [SearchController::class, 'search'])->name('search'); });