mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-02 22:46:16 +00:00
- 2.x - sites finishing
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
use App\Models\Queue;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class CreateQueue
|
||||
@ -16,15 +16,13 @@ class CreateQueue
|
||||
*/
|
||||
public function create(mixed $queueable, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
|
||||
$queue = new Queue([
|
||||
'server_id' => $queueable instanceof Server ? $queueable->id : $queueable->server_id,
|
||||
'site_id' => $queueable instanceof Site ? $queueable->id : null,
|
||||
'command' => $input['command'],
|
||||
'user' => $input['user'],
|
||||
'auto_start' => $input['auto_start'],
|
||||
'auto_restart' => $input['auto_restart'],
|
||||
'auto_start' => $input['auto_start'] ? 1 : 0,
|
||||
'auto_restart' => $input['auto_restart'] ? 1 : 0,
|
||||
'numprocs' => $input['numprocs'],
|
||||
'status' => QueueStatus::CREATING,
|
||||
]);
|
||||
@ -48,26 +46,18 @@ public function create(mixed $queueable, array $input): void
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validate(array $input): void
|
||||
public static function rules(Server $server): array
|
||||
{
|
||||
$rules = [
|
||||
return [
|
||||
'command' => [
|
||||
'required',
|
||||
],
|
||||
'user' => [
|
||||
'required',
|
||||
'in:root,'.config('core.ssh_user'),
|
||||
],
|
||||
'auto_start' => [
|
||||
'required',
|
||||
'in:0,1',
|
||||
],
|
||||
'auto_restart' => [
|
||||
'required',
|
||||
'in:0,1',
|
||||
Rule::in([
|
||||
'root',
|
||||
$server->ssh_user,
|
||||
]),
|
||||
],
|
||||
'numprocs' => [
|
||||
'required',
|
||||
@ -75,7 +65,5 @@ protected function validate(array $input): void
|
||||
'min:1',
|
||||
],
|
||||
];
|
||||
|
||||
Validator::make($input, $rules)->validate();
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,15 @@
|
||||
namespace App\Actions\Queue;
|
||||
|
||||
use App\Models\Queue;
|
||||
use App\SSH\Services\ProcessManager\ProcessManager;
|
||||
|
||||
class DeleteQueue
|
||||
{
|
||||
public function delete(Queue $queue): void
|
||||
{
|
||||
$queue->server->processManager()->handler()->delete($queue->id, $queue->site_id);
|
||||
/** @var ProcessManager $processManager */
|
||||
$processManager = $queue->server->processManager()->handler();
|
||||
$processManager->delete($queue->id, $queue->site_id);
|
||||
$queue->delete();
|
||||
}
|
||||
}
|
||||
|
72
app/Actions/Queue/EditQueue.php
Normal file
72
app/Actions/Queue/EditQueue.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Queue;
|
||||
|
||||
use App\Enums\QueueStatus;
|
||||
use App\Models\Queue;
|
||||
use App\Models\Server;
|
||||
use App\SSH\Services\ProcessManager\ProcessManager;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class EditQueue
|
||||
{
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function edit(Queue $queue, array $input): void
|
||||
{
|
||||
$queue->fill([
|
||||
'command' => $input['command'],
|
||||
'user' => $input['user'],
|
||||
'auto_start' => $input['auto_start'] ? 1 : 0,
|
||||
'auto_restart' => $input['auto_restart'] ? 1 : 0,
|
||||
'numprocs' => $input['numprocs'],
|
||||
'status' => QueueStatus::RESTARTING,
|
||||
]);
|
||||
$queue->save();
|
||||
|
||||
dispatch(function () use ($queue) {
|
||||
/** @var ProcessManager $processManager */
|
||||
$processManager = $queue->server->processManager()->handler();
|
||||
$processManager->delete($queue->id, $queue->site_id);
|
||||
|
||||
$processManager->create(
|
||||
$queue->id,
|
||||
$queue->command,
|
||||
$queue->user,
|
||||
$queue->auto_start,
|
||||
$queue->auto_restart,
|
||||
$queue->numprocs,
|
||||
$queue->getLogFile(),
|
||||
$queue->site_id
|
||||
);
|
||||
$queue->status = QueueStatus::RUNNING;
|
||||
$queue->save();
|
||||
})->catch(function () use ($queue) {
|
||||
$queue->status = QueueStatus::FAILED;
|
||||
$queue->save();
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
||||
public static function rules(Server $server): array
|
||||
{
|
||||
return [
|
||||
'command' => [
|
||||
'required',
|
||||
],
|
||||
'user' => [
|
||||
'required',
|
||||
Rule::in([
|
||||
'root',
|
||||
$server->ssh_user,
|
||||
]),
|
||||
],
|
||||
'numprocs' => [
|
||||
'required',
|
||||
'numeric',
|
||||
'min:1',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -4,10 +4,10 @@
|
||||
|
||||
use App\Enums\SslStatus;
|
||||
use App\Enums\SslType;
|
||||
use App\Models\ServerLog;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ssl;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
@ -18,7 +18,10 @@ class CreateSSL
|
||||
*/
|
||||
public function create(Site $site, array $input): void
|
||||
{
|
||||
$this->validate($input);
|
||||
$site->ssls()
|
||||
->where('type', $input['type'])
|
||||
->where('status', SslStatus::FAILED)
|
||||
->delete();
|
||||
|
||||
$ssl = new Ssl([
|
||||
'site_id' => $site->id,
|
||||
@ -32,6 +35,7 @@ public function create(Site $site, array $input): void
|
||||
if (isset($input['aliases']) && $input['aliases']) {
|
||||
$ssl->domains = array_merge($ssl->domains, $site->aliases);
|
||||
}
|
||||
$ssl->log_id = ServerLog::log($site->server, 'create-ssl', '', $site)->id;
|
||||
$ssl->save();
|
||||
|
||||
dispatch(function () use ($site, $ssl) {
|
||||
@ -47,10 +51,7 @@ public function create(Site $site, array $input): void
|
||||
})->onConnection('ssh');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function validate(array $input): void
|
||||
public static function rules(array $input): array
|
||||
{
|
||||
$rules = [
|
||||
'type' => [
|
||||
@ -61,9 +62,13 @@ protected function validate(array $input): void
|
||||
if (isset($input['type']) && $input['type'] == SslType::CUSTOM) {
|
||||
$rules['certificate'] = 'required';
|
||||
$rules['private'] = 'required';
|
||||
$rules['expires_at'] = 'required|date_format:Y-m-d|after_or_equal:'.now();
|
||||
$rules['expires_at'] = [
|
||||
'required',
|
||||
'date_format:Y-m-d',
|
||||
'after_or_equal:'.now(),
|
||||
];
|
||||
}
|
||||
|
||||
Validator::make($input, $rules)->validate();
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,15 @@
|
||||
namespace App\Actions\SSL;
|
||||
|
||||
use App\Models\Ssl;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
|
||||
class DeleteSSL
|
||||
{
|
||||
public function delete(Ssl $ssl): void
|
||||
{
|
||||
$ssl->site->server->webserver()->handler()->removeSSL($ssl);
|
||||
/** @var Webserver $webserver */
|
||||
$webserver = $ssl->site->server->webserver()->handler();
|
||||
$webserver->removeSSL($ssl);
|
||||
$ssl->delete();
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public function init(Server $server, ?string $asUser = null): self
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLog(ServerLog $log): self
|
||||
public function setLog(?ServerLog $log): self
|
||||
{
|
||||
$this->log = $log;
|
||||
|
||||
@ -93,7 +93,6 @@ public function connect(bool $sftp = false): void
|
||||
*/
|
||||
public function exec(string $command, string $log = '', ?int $siteId = null, ?bool $stream = false, ?callable $streamCallback = null): string
|
||||
{
|
||||
ds($command);
|
||||
if (! $this->log && $log) {
|
||||
$this->log = ServerLog::make($this->server, $log);
|
||||
if ($siteId) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\QueueStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
@ -45,6 +46,17 @@ class Queue extends AbstractModel
|
||||
'redirect_stderr' => 'boolean',
|
||||
];
|
||||
|
||||
public static array $statusColors = [
|
||||
QueueStatus::RUNNING => 'success',
|
||||
QueueStatus::CREATING => 'warning',
|
||||
QueueStatus::DELETING => 'warning',
|
||||
QueueStatus::FAILED => 'danger',
|
||||
QueueStatus::STARTING => 'warning',
|
||||
QueueStatus::STOPPING => 'warning',
|
||||
QueueStatus::RESTARTING => 'warning',
|
||||
QueueStatus::STOPPED => 'gray',
|
||||
];
|
||||
|
||||
public function getServerIdAttribute(int $value): int
|
||||
{
|
||||
if (! $value) {
|
||||
|
@ -114,7 +114,7 @@ public function getContent($lines = null): ?string
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function log(Server $server, string $type, string $content, ?Site $site = null): void
|
||||
public static function log(Server $server, string $type, string $content, ?Site $site = null): static
|
||||
{
|
||||
$log = new static([
|
||||
'server_id' => $server->id,
|
||||
@ -125,6 +125,8 @@ public static function log(Server $server, string $type, string $content, ?Site
|
||||
]);
|
||||
$log->save();
|
||||
$log->write($content);
|
||||
|
||||
return $log;
|
||||
}
|
||||
|
||||
public static function make(Server $server, string $type): ServerLog
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SslStatus;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@ -18,6 +19,8 @@
|
||||
* @property Site $site
|
||||
* @property string $ca_path
|
||||
* @property ?array $domains
|
||||
* @property int $log_id
|
||||
* @property ?ServerLog $log
|
||||
*/
|
||||
class Ssl extends AbstractModel
|
||||
{
|
||||
@ -32,6 +35,7 @@ class Ssl extends AbstractModel
|
||||
'expires_at',
|
||||
'status',
|
||||
'domains',
|
||||
'log_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -41,6 +45,14 @@ class Ssl extends AbstractModel
|
||||
'ca' => 'encrypted',
|
||||
'expires_at' => 'datetime',
|
||||
'domains' => 'array',
|
||||
'log_id' => 'integer',
|
||||
];
|
||||
|
||||
public static array $statusColors = [
|
||||
SslStatus::CREATED => 'success',
|
||||
SslStatus::CREATING => 'warning',
|
||||
SslStatus::DELETING => 'warning',
|
||||
SslStatus::FAILED => 'danger',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
@ -126,4 +138,9 @@ public function getDomains(): array
|
||||
|
||||
return $this->domains;
|
||||
}
|
||||
|
||||
public function log(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ServerLog::class);
|
||||
}
|
||||
}
|
||||
|
55
app/Policies/QueuePolicy.php
Normal file
55
app/Policies/QueuePolicy.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Queue;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class QueuePolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function viewAny(User $user, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$server->isReady() &&
|
||||
$site->isReady();
|
||||
}
|
||||
|
||||
public function view(User $user, Queue $queue, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$queue->site_id === $site->id;
|
||||
}
|
||||
|
||||
public function create(User $user, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$server->isReady() &&
|
||||
$site->isReady();
|
||||
}
|
||||
|
||||
public function update(User $user, Queue $queue, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$queue->site_id === $site->id;
|
||||
}
|
||||
|
||||
public function delete(User $user, Queue $queue, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$queue->site_id === $site->id;
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\User;
|
||||
@ -36,52 +35,4 @@ public function delete(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) && $service->server->isReady();
|
||||
}
|
||||
|
||||
public function start(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) &&
|
||||
$service->server->isReady() &&
|
||||
in_array($service->status, [
|
||||
ServiceStatus::STOPPED,
|
||||
ServiceStatus::FAILED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function stop(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) &&
|
||||
$service->server->isReady() &&
|
||||
in_array($service->status, [
|
||||
ServiceStatus::READY,
|
||||
ServiceStatus::FAILED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function restart(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) &&
|
||||
$service->server->isReady() &&
|
||||
in_array($service->status, [
|
||||
ServiceStatus::READY,
|
||||
ServiceStatus::FAILED,
|
||||
ServiceStatus::STOPPED,
|
||||
]);
|
||||
}
|
||||
|
||||
public function enable(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) &&
|
||||
$service->server->isReady() &&
|
||||
$service->status == ServiceStatus::DISABLED;
|
||||
}
|
||||
|
||||
public function disable(User $user, Service $service): bool
|
||||
{
|
||||
return ($user->isAdmin() || $service->server->project->users->contains($user)) &&
|
||||
$service->server->isReady() &&
|
||||
in_array($service->status, [
|
||||
ServiceStatus::READY,
|
||||
ServiceStatus::STOPPED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
55
app/Policies/SslPolicy.php
Normal file
55
app/Policies/SslPolicy.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ssl;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class SslPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function viewAny(User $user, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$server->isReady() &&
|
||||
$site->isReady();
|
||||
}
|
||||
|
||||
public function view(User $user, Ssl $ssl, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$ssl->site_id === $site->id;
|
||||
}
|
||||
|
||||
public function create(User $user, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$server->isReady() &&
|
||||
$site->isReady();
|
||||
}
|
||||
|
||||
public function update(User $user, Ssl $ssl, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$ssl->site_id === $site->id;
|
||||
}
|
||||
|
||||
public function delete(User $user, Ssl $ssl, Site $site, Server $server): bool
|
||||
{
|
||||
return ($user->isAdmin() || $server->project->users->contains($user)) &&
|
||||
$site->server_id === $server->id &&
|
||||
$server->isReady() &&
|
||||
$site->isReady() &&
|
||||
$ssl->site_id === $site->id;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\SSH\Services\Webserver;
|
||||
|
||||
use App\Exceptions\SSHError;
|
||||
use App\Exceptions\SSLCreationException;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ssl;
|
||||
@ -113,7 +114,7 @@ public function changePHPVersion(Site $site, $version): void
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SSLCreationException
|
||||
* @throws SSHError
|
||||
*/
|
||||
public function setupSSL(Ssl $ssl): void
|
||||
{
|
||||
@ -136,7 +137,7 @@ public function setupSSL(Ssl $ssl): void
|
||||
'pk_path' => $ssl->getPkPath(),
|
||||
]);
|
||||
}
|
||||
$result = $this->service->server->ssh()->exec(
|
||||
$result = $this->service->server->ssh()->setLog($ssl->log)->exec(
|
||||
$command,
|
||||
'create-ssl',
|
||||
$ssl->site_id
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use App\Exceptions\SSHError;
|
||||
use App\Helpers\HtmxResponse;
|
||||
use Filament\Notifications\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
|
||||
function generate_public_key($privateKeyPath, $publicKeyPath): void
|
||||
@ -67,6 +68,13 @@ function run_action(object $static, Closure $callback): void
|
||||
->danger()
|
||||
->title($e->getMessage())
|
||||
->body($e->getLog()?->getContent(30))
|
||||
->actions([
|
||||
Action::make('View Logs')
|
||||
->url(App\Web\Pages\Servers\Logs\Index::getUrl([
|
||||
'server' => $e->getLog()?->server_id,
|
||||
]))
|
||||
->openUrlInNewTab(),
|
||||
])
|
||||
->send();
|
||||
|
||||
if (method_exists($static, 'halt')) {
|
||||
|
@ -56,7 +56,7 @@ protected function getHeaderActions(): array
|
||||
Select::make('user')
|
||||
->rules(fn (callable $get) => CreateCronJob::rules($get())['user'])
|
||||
->options([
|
||||
'vito' => 'vito',
|
||||
'vito' => $this->server->ssh_user,
|
||||
'root' => 'root',
|
||||
]),
|
||||
Select::make('frequency')
|
||||
|
@ -16,7 +16,6 @@
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Throwable;
|
||||
|
||||
class Index extends Page
|
||||
{
|
||||
@ -228,21 +227,12 @@ protected function getHeaderActions(): array
|
||||
]),
|
||||
])
|
||||
->modalSubmitActionLabel('Create')
|
||||
->action(function ($input) {
|
||||
$this->authorize('create', Server::class);
|
||||
|
||||
$this->validate();
|
||||
|
||||
try {
|
||||
$server = app(CreateServerAction::class)->create(auth()->user(), $input);
|
||||
->action(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
$server = app(CreateServerAction::class)->create(auth()->user(), $data);
|
||||
|
||||
$this->redirect(View::getUrl(['server' => $server]));
|
||||
} catch (Throwable $e) {
|
||||
Notification::make()
|
||||
->title($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ class LogsList extends Widget
|
||||
|
||||
public ?string $label = '';
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return ServerLog::query()
|
||||
|
@ -11,7 +11,7 @@
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\ActionGroup;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget;
|
||||
@ -33,9 +33,10 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
ImageColumn::make('image_url')
|
||||
IconColumn::make('id')
|
||||
->label('Service')
|
||||
->size(24),
|
||||
->icon(fn (Service $record) => 'icon-'.$record->name)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->sortable(),
|
||||
TextColumn::make('version')
|
||||
@ -59,21 +60,22 @@ public function getTable(): Table
|
||||
return $this->table
|
||||
->actions([
|
||||
ActionGroup::make([
|
||||
$this->serviceAction('start'),
|
||||
$this->serviceAction('stop'),
|
||||
$this->serviceAction('restart'),
|
||||
$this->serviceAction('disable'),
|
||||
$this->serviceAction('enable'),
|
||||
$this->serviceAction('start', 'heroicon-o-play'),
|
||||
$this->serviceAction('stop', 'heroicon-o-stop'),
|
||||
$this->serviceAction('restart', 'heroicon-o-arrow-path'),
|
||||
$this->serviceAction('disable', 'heroicon-o-x-mark'),
|
||||
$this->serviceAction('enable', 'heroicon-o-check'),
|
||||
$this->uninstallAction(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
private function serviceAction(string $type): Action
|
||||
private function serviceAction(string $type, string $icon): Action
|
||||
{
|
||||
return Action::make($type)
|
||||
->authorize(fn (Service $service) => auth()->user()?->can($type, $service))
|
||||
->authorize(fn (Service $service) => auth()->user()?->can('update', $service))
|
||||
->label(ucfirst($type).' Service')
|
||||
->icon($icon)
|
||||
->action(function (Service $service) use ($type) {
|
||||
try {
|
||||
app(Manage::class)->$type($service);
|
||||
@ -95,6 +97,7 @@ private function uninstallAction(): Action
|
||||
return Action::make('uninstall')
|
||||
->authorize(fn (Service $service) => auth()->user()?->can('delete', $service))
|
||||
->label('Uninstall Service')
|
||||
->icon('heroicon-o-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->action(function (Service $service) {
|
||||
|
@ -2,7 +2,15 @@
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites\Pages\Queues;
|
||||
|
||||
use App\Actions\Queue\CreateQueue;
|
||||
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
|
||||
{
|
||||
@ -17,6 +25,54 @@ public static function canAccess(): bool
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [];
|
||||
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.html')
|
||||
->openUrlInNewTab(),
|
||||
CreateAction::make('create')
|
||||
->icon('heroicon-o-plus')
|
||||
->createAnother(false)
|
||||
->modalWidth(MaxWidth::ExtraLarge)
|
||||
->label('New Queue')
|
||||
->form([
|
||||
TextInput::make('command')
|
||||
->rules(CreateQueue::rules($this->server)['command'])
|
||||
->helperText('Example: php /home/vito/your-site/artisan queue:work'),
|
||||
Select::make('user')
|
||||
->rules(fn (callable $get) => CreateQueue::rules($this->server)['user'])
|
||||
->options([
|
||||
'vito' => $this->server->ssh_user,
|
||||
'root' => 'root',
|
||||
]),
|
||||
TextInput::make('numprocs')
|
||||
->default(1)
|
||||
->rules(CreateQueue::rules($this->server)['numprocs'])
|
||||
->helperText('Number of processes'),
|
||||
Grid::make()
|
||||
->schema([
|
||||
Checkbox::make('auto_start')
|
||||
->default(false),
|
||||
Checkbox::make('auto_restart')
|
||||
->default(false),
|
||||
]),
|
||||
])
|
||||
->using(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(CreateQueue::class)->create($this->site, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
160
app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php
Normal file
160
app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?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 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;
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Queue::query()->where('site_id', $this->site->id);
|
||||
}
|
||||
|
||||
protected static ?string $heading = '';
|
||||
|
||||
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 getTable(): Table
|
||||
{
|
||||
return $this->table
|
||||
->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
|
||||
{
|
||||
return Action::make($type)
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('update', [$record, $this->site, $this->site->server]))
|
||||
->label(ucfirst($type).' queue')
|
||||
->icon($icon)
|
||||
->action(function (Queue $record) use ($type) {
|
||||
run_action($this, function () use ($record, $type) {
|
||||
app(ManageQueue::class)->$type($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function logsAction(): Action
|
||||
{
|
||||
return Action::make('logs')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('view', [$record, $this->site, $this->site->server]))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Queue $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => app(GetQueueLogs::class)->getLogs($record),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close');
|
||||
}
|
||||
|
||||
private function editAction(): Action
|
||||
{
|
||||
return EditAction::make('edit')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('update', [$record, $this->site, $this->site->server]))
|
||||
->modalWidth(MaxWidth::ExtraLarge)
|
||||
->fillForm(fn (Queue $record) => [
|
||||
'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) {
|
||||
run_action($this, function () use ($record, $data) {
|
||||
app(EditQueue::class)->edit($record, $data);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function deleteAction(): Action
|
||||
{
|
||||
return DeleteAction::make('delete')
|
||||
->icon('heroicon-o-trash')
|
||||
->authorize(fn (Queue $record) => auth()->user()->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Queue $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
app(DeleteQueue::class)->delete($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -2,7 +2,17 @@
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites\Pages\SSL;
|
||||
|
||||
use App\Actions\SSL\CreateSSL;
|
||||
use App\Models\Ssl;
|
||||
use App\Web\Pages\Servers\Sites\Page;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\DatePicker;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
|
||||
class Index extends Page
|
||||
{
|
||||
@ -12,11 +22,60 @@ class Index extends Page
|
||||
|
||||
public static function canAccess(): bool
|
||||
{
|
||||
return true;
|
||||
return auth()->user()?->can('viewAny', [Ssl::class, static::getSiteFromRoute(), static::getServerFromRoute()]) ?? false;
|
||||
}
|
||||
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
[Widgets\SslsList::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/ssl.html')
|
||||
->openUrlInNewTab(),
|
||||
CreateAction::make('create')
|
||||
->label('New Certificate')
|
||||
->icon('heroicon-o-lock-closed')
|
||||
->form([
|
||||
Select::make('type')
|
||||
->options(
|
||||
collect(config('core.ssl_types'))->mapWithKeys(fn ($type) => [$type => $type])
|
||||
)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['type'])
|
||||
->reactive(),
|
||||
Textarea::make('certificate')
|
||||
->rows(5)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['certificate'])
|
||||
->visible(fn (Get $get) => $get('type') === 'custom'),
|
||||
Textarea::make('private')
|
||||
->label('Private Key')
|
||||
->rows(5)
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['private'])
|
||||
->visible(fn (Get $get) => $get('type') === 'custom'),
|
||||
DatePicker::make('expires_at')
|
||||
->format('Y-m-d')
|
||||
->rules(fn (Get $get) => CreateSSL::rules($get())['expires_at'])
|
||||
->visible(fn (Get $get) => $get('type') === 'custom'),
|
||||
Checkbox::make('aliases')
|
||||
->label("Set SSL for site's aliases as well"),
|
||||
])
|
||||
->createAnother(false)
|
||||
->modalWidth(MaxWidth::Large)
|
||||
->using(function (array $data) {
|
||||
run_action($this, function () use ($data) {
|
||||
app(CreateSSL::class)->create($this->site, $data);
|
||||
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
81
app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php
Normal file
81
app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites\Pages\SSL\Widgets;
|
||||
|
||||
use App\Actions\SSL\DeleteSSL;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ssl;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
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 SslsList extends Widget
|
||||
{
|
||||
public Site $site;
|
||||
|
||||
protected $listeners = ['$refresh'];
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Ssl::query()->where('site_id', $this->site->id);
|
||||
}
|
||||
|
||||
protected static ?string $heading = '';
|
||||
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
TextColumn::make('type')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->formatStateUsing(fn (Ssl $record) => $record->created_at_by_timezone)
|
||||
->sortable(),
|
||||
TextColumn::make('expires_at')
|
||||
->formatStateUsing(fn (Ssl $record) => $record->getDateTimeByTimezone($record->expires_at))
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->color(fn (Ssl $record) => Ssl::$statusColors[$record->status])
|
||||
->searchable()
|
||||
->sortable(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTable(): Table
|
||||
{
|
||||
return $this->table
|
||||
->actions([
|
||||
Action::make('logs')
|
||||
->hiddenLabel()
|
||||
->tooltip('Logs')
|
||||
->icon('heroicon-o-eye')
|
||||
->authorize(fn (Ssl $record) => auth()->user()->can('view', [$record, $this->site, $this->site->server]))
|
||||
->modalHeading('View Log')
|
||||
->modalContent(function (Ssl $record) {
|
||||
return view('components.console-view', [
|
||||
'slot' => $record->log?->getContent(),
|
||||
'attributes' => new ComponentAttributeBag,
|
||||
]);
|
||||
})
|
||||
->modalSubmitAction(false)
|
||||
->modalCancelActionLabel('Close'),
|
||||
DeleteAction::make('delete')
|
||||
->hiddenLabel()
|
||||
->tooltip('Delete')
|
||||
->icon('heroicon-o-trash')
|
||||
->authorize(fn (Ssl $record) => auth()->user()->can('delete', [$record, $this->site, $this->site->server]))
|
||||
->using(function (Ssl $record) {
|
||||
run_action($this, function () use ($record) {
|
||||
app(DeleteSSL::class)->delete($record);
|
||||
$this->dispatch('$refresh');
|
||||
});
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Web\Pages\Servers\Sites;
|
||||
|
||||
use App\Actions\Site\DeleteSite;
|
||||
use App\SSH\Services\Webserver\Webserver;
|
||||
use App\Web\Fields\CodeEditorField;
|
||||
use Filament\Actions\Action;
|
||||
@ -40,16 +41,23 @@ private function deleteAction(): Action
|
||||
{
|
||||
return DeleteAction::make()
|
||||
->icon('heroicon-o-trash')
|
||||
->record($this->server)
|
||||
->record($this->site)
|
||||
->modalHeading('Delete Site')
|
||||
->modalDescription('Once your site is deleted, all of its resources and data will be permanently deleted and can\'t be restored');
|
||||
->modalDescription('Once your site is deleted, all of its resources and data will be permanently deleted and can\'t be restored')
|
||||
->using(function () {
|
||||
run_action($this, function () {
|
||||
app(DeleteSite::class)->delete($this->site);
|
||||
|
||||
$this->redirect(Index::getUrl(['server' => $this->server]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function vhostAction(): Action
|
||||
{
|
||||
return Action::make('vhost')
|
||||
->color('gray')
|
||||
->icon('si-nginx')
|
||||
->icon('icon-nginx')
|
||||
->label('VHost')
|
||||
->modalSubmitActionLabel('Save')
|
||||
->form([
|
||||
|
@ -79,11 +79,19 @@ public function getWidgets(): array
|
||||
|
||||
public function getHeaderActions(): array
|
||||
{
|
||||
$actions = [];
|
||||
$actions = [
|
||||
Action::make('read-the-docs')
|
||||
->label('Read the Docs')
|
||||
->icon('heroicon-o-document-text')
|
||||
->color('gray')
|
||||
->url('https://vitodeploy.com/sites/application.html')
|
||||
->openUrlInNewTab(),
|
||||
];
|
||||
$actionsGroup = [];
|
||||
|
||||
if (in_array(SiteFeature::DEPLOYMENT, $this->site->type()->supportedFeatures())) {
|
||||
$actions[] = $this->deployAction();
|
||||
$actionsGroup[] = $this->autoDeploymentAction();
|
||||
$actionsGroup[] = $this->deploymentScriptAction();
|
||||
}
|
||||
|
||||
@ -130,6 +138,27 @@ private function deployAction(): Action
|
||||
});
|
||||
}
|
||||
|
||||
private function autoDeploymentAction(): Action
|
||||
{
|
||||
return Action::make('auto-deployment')
|
||||
->label(fn () => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalHeading(fn () => $this->site->isAutoDeployment() ? 'Disable Auto Deployment' : 'Enable Auto Deployment')
|
||||
->modalIconColor(fn () => $this->site->isAutoDeployment() ? 'red' : 'green')
|
||||
->requiresConfirmation()
|
||||
->action(function () {
|
||||
run_action($this, function () {
|
||||
$this->site->isAutoDeployment()
|
||||
? $this->site->disableAutoDeployment()
|
||||
: $this->site->enableAutoDeployment();
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Auto deployment '.($this->site->isAutoDeployment() ? 'disabled' : 'enabled').'!')
|
||||
->send();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function deploymentScriptAction(): Action
|
||||
{
|
||||
return Action::make('deployment-script')
|
||||
|
@ -7,6 +7,7 @@
|
||||
use App\Web\Pages\Servers\Sites\Settings;
|
||||
use App\Web\Pages\Servers\Sites\View;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
@ -26,6 +27,10 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('type')
|
||||
->icon(fn (Site $record) => 'icon-'.$record->type)
|
||||
->tooltip(fn (Site $record) => $record->type)
|
||||
->width(24),
|
||||
TextColumn::make('domain')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
@ -6,6 +6,7 @@
|
||||
use App\Web\Pages\Servers\Settings;
|
||||
use App\Web\Pages\Servers\View;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
@ -23,6 +24,10 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (Server $record) => 'icon-'.$record->provider)
|
||||
->tooltip(fn (Server $record) => $record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
@ -8,7 +8,7 @@
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
@ -26,9 +26,9 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
ImageColumn::make('image_url')
|
||||
->label('Provider')
|
||||
->size(24),
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (ServerProvider $record) => 'icon-'.$record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->default(fn ($record) => $record->profile)
|
||||
->label('Name')
|
||||
|
@ -8,7 +8,7 @@
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
@ -28,9 +28,9 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
ImageColumn::make('image_url')
|
||||
->label('Provider')
|
||||
->size(24),
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (SourceControl $record) => 'icon-'.$record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->default(fn (SourceControl $record) => $record->profile)
|
||||
->label('Name')
|
||||
|
@ -8,7 +8,7 @@
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\DeleteAction;
|
||||
use Filament\Tables\Actions\EditAction;
|
||||
use Filament\Tables\Columns\ImageColumn;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as Widget;
|
||||
@ -26,11 +26,12 @@ protected function getTableQuery(): Builder
|
||||
protected function getTableColumns(): array
|
||||
{
|
||||
return [
|
||||
ImageColumn::make('image_url')
|
||||
->label('Provider')
|
||||
->size(24),
|
||||
IconColumn::make('provider')
|
||||
->icon(fn (StorageProvider $record) => 'icon-'.$record->provider)
|
||||
->tooltip(fn (StorageProvider $record) => $record->provider)
|
||||
->width(24),
|
||||
TextColumn::make('name')
|
||||
->default(fn ($record) => $record->profile)
|
||||
->default(fn (StorageProvider $record) => $record->profile)
|
||||
->label('Name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
@ -5,57 +5,91 @@
|
||||
use App\Actions\Tag\SyncTags;
|
||||
use App\Models\Server;
|
||||
use App\Models\Site;
|
||||
use Filament\Forms\Components\Actions\Action as FormAction;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Infolists\Components\Actions\Action;
|
||||
use Filament\Infolists\Components\Actions\Action as InfolistAction;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
use Filament\Tables\Actions\Action as TableAction;
|
||||
|
||||
class EditTags
|
||||
{
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
*/
|
||||
public static function infolist(mixed $taggable): Action
|
||||
public static function infolist(mixed $taggable): InfolistAction
|
||||
{
|
||||
return Action::make('edit_tags')
|
||||
return InfolistAction::make('edit_tags')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->tooltip('Edit Tags')
|
||||
->hiddenLabel()
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Edit Tags')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form([
|
||||
Select::make('tags')
|
||||
->default($taggable->tags()->pluck('tags.id')->toArray())
|
||||
->options(function () {
|
||||
return auth()->user()->currentProject->tags()->pluck('name', 'id')->toArray();
|
||||
})
|
||||
->nestedRecursiveRules(SyncTags::rules(auth()->user()->currentProject->id)['tags.*'])
|
||||
->suffixAction(
|
||||
\Filament\Forms\Components\Actions\Action::make('create_tag')
|
||||
->icon('heroicon-o-plus')
|
||||
->tooltip('Create a new tag')
|
||||
->modalSubmitActionLabel('Create')
|
||||
->modalHeading('Create Tag')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(Create::form())
|
||||
->action(function (array $data) {
|
||||
Create::action($data);
|
||||
}),
|
||||
)
|
||||
->multiple(),
|
||||
])
|
||||
->action(function (array $data) use ($taggable) {
|
||||
app(SyncTags::class)->sync(auth()->user(), [
|
||||
'taggable_id' => $taggable->id,
|
||||
'taggable_type' => get_class($taggable),
|
||||
'tags' => $data['tags'],
|
||||
]);
|
||||
->form(static::form($taggable))
|
||||
->action(static::action($taggable));
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Tags updated!')
|
||||
->send();
|
||||
});
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
*/
|
||||
public static function table(mixed $taggable): TableAction
|
||||
{
|
||||
return TableAction::make('edit_tags')
|
||||
->icon('heroicon-o-pencil-square')
|
||||
->tooltip('Edit Tags')
|
||||
->hiddenLabel()
|
||||
->modalSubmitActionLabel('Save')
|
||||
->modalHeading('Edit Tags')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(static::form($taggable))
|
||||
->action(static::action($taggable));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
*/
|
||||
private static function form(mixed $taggable): array
|
||||
{
|
||||
return [
|
||||
Select::make('tags')
|
||||
->default($taggable->tags()->pluck('tags.id')->toArray())
|
||||
->options(function () {
|
||||
return auth()->user()->currentProject->tags()->pluck('name', 'id')->toArray();
|
||||
})
|
||||
->nestedRecursiveRules(SyncTags::rules(auth()->user()->currentProject->id)['tags.*'])
|
||||
->suffixAction(
|
||||
FormAction::make('create_tag')
|
||||
->icon('heroicon-o-plus')
|
||||
->tooltip('Create a new tag')
|
||||
->modalSubmitActionLabel('Create')
|
||||
->modalHeading('Create Tag')
|
||||
->modalWidth(MaxWidth::Medium)
|
||||
->form(Create::form())
|
||||
->action(function (array $data) {
|
||||
Create::action($data);
|
||||
}),
|
||||
)
|
||||
->multiple(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Site|Server $taggable
|
||||
*/
|
||||
private static function action(mixed $taggable): \Closure
|
||||
{
|
||||
return function (array $data) use ($taggable) {
|
||||
app(SyncTags::class)->sync(auth()->user(), [
|
||||
'taggable_id' => $taggable->id,
|
||||
'taggable_type' => get_class($taggable),
|
||||
'tags' => $data['tags'],
|
||||
]);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('Tags updated!')
|
||||
->send();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user