Add workers to servers (#547)

This commit is contained in:
Saeed Vaziry
2025-03-16 14:09:15 +01:00
committed by GitHub
parent 48ae561ea4
commit 72352aad8d
42 changed files with 603 additions and 454 deletions

View File

@ -2,11 +2,11 @@
namespace App\Web\Pages\Servers\Sites;
use App\Models\Queue;
use App\Models\ServerLog;
use App\Models\Site;
use App\Models\Ssl;
use App\Models\User;
use App\Models\Worker;
use App\Web\Contracts\HasSecondSubNav;
use App\Web\Pages\Servers\Page as BasePage;
use App\Web\Pages\Servers\Sites\Widgets\SiteSummary;
@ -45,11 +45,11 @@ public function getSecondSubNavigation(): array
]));
}
if ($user->can('viewAny', [Queue::class, $this->site, $this->server])) {
$items[] = NavigationItem::make(Pages\Queues\Index::getNavigationLabel())
if ($user->can('viewAny', [Worker::class, $this->server, $this->site])) {
$items[] = NavigationItem::make(Pages\Workers\Index::getNavigationLabel())
->icon('heroicon-o-queue-list')
->isActiveWhen(fn () => request()->routeIs(Pages\Queues\Index::getRouteName()))
->url(Pages\Queues\Index::getUrl(parameters: [
->isActiveWhen(fn () => request()->routeIs(Pages\Workers\Index::getRouteName()))
->url(Pages\Workers\Index::getUrl(parameters: [
'server' => $this->server,
'site' => $this->site,
]));

View File

@ -1,79 +0,0 @@
<?php
namespace App\Web\Pages\Servers\Sites\Pages\Queues;
use App\Actions\Queue\CreateQueue;
use App\Models\Queue;
use App\Web\Pages\Servers\Sites\Page;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\MaxWidth;
class Index extends Page
{
protected static ?string $slug = 'servers/{server}/sites/{site}/queues';
protected static ?string $title = 'Queues';
public function mount(): void
{
$this->authorize('viewAny', [Queue::class, $this->site, $this->server]);
}
public function getWidgets(): array
{
return [
[Widgets\QueuesList::class, ['site' => $this->site]],
];
}
protected function getHeaderActions(): array
{
return [
Action::make('read-the-docs')
->label('Read the Docs')
->icon('heroicon-o-document-text')
->color('gray')
->url('https://vitodeploy.com/sites/queues')
->openUrlInNewTab(),
CreateAction::make('create')
->icon('heroicon-o-plus')
->createAnother(false)
->modalWidth(MaxWidth::ExtraLarge)
->label('New Queue')
->form([
TextInput::make('command')
->rules(CreateQueue::rules($this->site)['command'])
->helperText('Example: php /home/vito/your-site/artisan queue:work'),
Select::make('user')
->rules(fn (callable $get) => CreateQueue::rules($this->site)['user'])
->options([
'root' => 'root',
$this->site->user => $this->site->user,
]),
TextInput::make('numprocs')
->default(1)
->rules(CreateQueue::rules($this->site)['numprocs'])
->helperText('Number of processes'),
Grid::make()
->schema([
Checkbox::make('auto_start')
->default(false),
Checkbox::make('auto_restart')
->default(false),
]),
])
->using(function (array $data): void {
run_action($this, function () use ($data): void {
app(CreateQueue::class)->create($this->site, $data);
$this->dispatch('$refresh');
});
}),
];
}
}

View File

@ -1,178 +0,0 @@
<?php
namespace App\Web\Pages\Servers\Sites\Pages\Queues\Widgets;
use App\Actions\Queue\DeleteQueue;
use App\Actions\Queue\EditQueue;
use App\Actions\Queue\GetQueueLogs;
use App\Actions\Queue\ManageQueue;
use App\Models\Queue;
use App\Models\Site;
use App\Models\User;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\MaxWidth;
use Filament\Tables\Actions\Action;
use Filament\Tables\Actions\ActionGroup;
use Filament\Tables\Actions\DeleteAction;
use Filament\Tables\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as Widget;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\View\ComponentAttributeBag;
class QueuesList extends Widget
{
public Site $site;
/**
* @var array<string>
*/
protected $listeners = ['$refresh'];
/**
* @return Builder<Queue>
*/
protected function getTableQuery(): Builder
{
return Queue::query()->where('site_id', $this->site->id);
}
protected function getTableColumns(): array
{
return [
TextColumn::make('command')
->limit(20)
->copyable()
->tooltip(fn (Queue $record) => $record->command)
->searchable()
->sortable(),
TextColumn::make('created_at')
->formatStateUsing(fn (Queue $record) => $record->created_at_by_timezone)
->sortable(),
TextColumn::make('status')
->label('Status')
->badge()
->color(fn (Queue $record) => Queue::$statusColors[$record->status])
->searchable()
->sortable(),
];
}
public function table(Table $table): Table
{
return $table
->heading(null)
->query($this->getTableQuery())
->columns($this->getTableColumns())
->actions([
ActionGroup::make([
$this->editAction(),
$this->operationAction('start', 'heroicon-o-play'),
$this->operationAction('stop', 'heroicon-o-stop'),
$this->operationAction('restart', 'heroicon-o-arrow-path'),
$this->logsAction(),
$this->deleteAction(),
]),
]);
}
private function operationAction(string $type, string $icon): Action
{
/** @var User $user */
$user = auth()->user();
return Action::make($type)
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server]))
->label(ucfirst($type).' queue')
->icon($icon)
->action(function (Queue $record) use ($type): void {
run_action($this, function () use ($record, $type): void {
app(ManageQueue::class)->$type($record);
$this->dispatch('$refresh');
});
});
}
private function logsAction(): Action
{
/** @var User $user */
$user = auth()->user();
return Action::make('logs')
->icon('heroicon-o-eye')
->authorize(fn (Queue $record) => $user->can('view', [$record, $this->site, $this->site->server]))
->modalHeading('View Log')
->modalContent(fn (Queue $record) => view('components.console-view', [
'slot' => app(GetQueueLogs::class)->getLogs($record),
'attributes' => new ComponentAttributeBag,
]))
->modalSubmitAction(false)
->modalCancelActionLabel('Close');
}
private function editAction(): Action
{
/** @var User $user */
$user = auth()->user();
return EditAction::make('edit')
->icon('heroicon-o-pencil-square')
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server]))
->modalWidth(MaxWidth::ExtraLarge)
->fillForm(fn (Queue $record): array => [
'command' => $record->command,
'user' => $record->user,
'numprocs' => $record->numprocs,
'auto_start' => $record->auto_start,
'auto_restart' => $record->auto_restart,
])
->form([
TextInput::make('command')
->rules(EditQueue::rules($this->site->server)['command'])
->helperText('Example: php /home/vito/your-site/artisan queue:work'),
Select::make('user')
->rules(fn (callable $get) => EditQueue::rules($this->site->server)['user'])
->options([
'vito' => $this->site->server->ssh_user,
'root' => 'root',
]),
TextInput::make('numprocs')
->default(1)
->rules(EditQueue::rules($this->site->server)['numprocs'])
->helperText('Number of processes'),
Grid::make()
->schema([
Checkbox::make('auto_start')
->default(false),
Checkbox::make('auto_restart')
->default(false),
]),
])
->using(function (Queue $record, array $data): void {
run_action($this, function () use ($record, $data): void {
app(EditQueue::class)->edit($record, $data);
$this->dispatch('$refresh');
});
});
}
private function deleteAction(): Action
{
/** @var User $user */
$user = auth()->user();
return DeleteAction::make('delete')
->icon('heroicon-o-trash')
->authorize(fn (Queue $record) => $user->can('delete', [$record, $this->site, $this->site->server]))
->using(function (Queue $record): void {
run_action($this, function () use ($record): void {
app(DeleteQueue::class)->delete($record);
$this->dispatch('$refresh');
});
});
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace App\Web\Pages\Servers\Sites\Pages\Workers;
use App\Models\Worker;
use App\Web\Pages\Servers\Sites\Page;
use App\Web\Pages\Servers\Workers\Actions\Create;
use Filament\Actions\Action;
use Filament\Actions\CreateAction;
use Filament\Support\Enums\MaxWidth;
class Index extends Page
{
protected static ?string $slug = 'servers/{server}/sites/{site}/workers';
protected static ?string $title = 'Workers';
public function mount(): void
{
$this->authorize('viewAny', [Worker::class, $this->server, $this->site]);
}
public function getWidgets(): array
{
return [
[Widgets\WorkersList::class, [
'server' => $this->server,
'site' => $this->site,
]],
];
}
protected function getHeaderActions(): array
{
return [
Action::make('read-the-docs')
->label('Read the Docs')
->icon('heroicon-o-document-text')
->color('gray')
->url('https://vitodeploy.com/servers/workers')
->openUrlInNewTab(),
CreateAction::make('create')
->icon('heroicon-o-plus')
->createAnother(false)
->modalWidth(MaxWidth::ExtraLarge)
->label('New Worker')
->form(Create::form($this->server, $this->site))
->using(fn (array $data) => run_action($this, function () use ($data): void {
Create::action($this, $data, $this->server, $this->site);
})),
];
}
}

View File

@ -0,0 +1,5 @@
<?php
namespace App\Web\Pages\Servers\Sites\Pages\Workers\Widgets;
class WorkersList extends \App\Web\Pages\Servers\Workers\Widgets\WorkersList {}