From c24b4b7333e4b675c8366878bd15a8fcc5bb7453 Mon Sep 17 00:00:00 2001 From: Saeed Vaziry Date: Sun, 6 Oct 2024 16:06:51 +0200 Subject: [PATCH] - 2.x - sites finishing --- app/Actions/Queue/CreateQueue.php | 30 +-- app/Actions/Queue/DeleteQueue.php | 5 +- app/Actions/Queue/EditQueue.php | 72 +++++++ app/Actions/SSL/CreateSSL.php | 21 +- app/Actions/SSL/DeleteSSL.php | 5 +- app/Helpers/SSH.php | 3 +- app/Models/Queue.php | 12 ++ app/Models/ServerLog.php | 4 +- app/Models/Ssl.php | 17 ++ app/Policies/QueuePolicy.php | 55 ++++++ app/Policies/ServicePolicy.php | 49 ----- app/Policies/SslPolicy.php | 55 ++++++ app/SSH/Services/Webserver/Nginx.php | 5 +- app/Support/helpers.php | 8 + app/Web/Pages/Servers/CronJobs/Index.php | 2 +- app/Web/Pages/Servers/Index.php | 18 +- .../Pages/Servers/Logs/Widgets/LogsList.php | 2 + .../Servers/Services/Widgets/ServicesList.php | 23 ++- .../Servers/Sites/Pages/Queues/Index.php | 58 +++++- .../Sites/Pages/Queues/Widgets/QueuesList.php | 160 +++++++++++++++ .../Pages/Servers/Sites/Pages/SSL/Index.php | 63 +++++- .../Sites/Pages/SSL/Widgets/SslsList.php | 81 ++++++++ app/Web/Pages/Servers/Sites/Settings.php | 14 +- app/Web/Pages/Servers/Sites/View.php | 31 ++- .../Pages/Servers/Sites/Widgets/SitesList.php | 5 + app/Web/Pages/Servers/Widgets/ServersList.php | 5 + .../Widgets/ServerProvidersList.php | 8 +- .../Widgets/SourceControlsList.php | 8 +- .../Widgets/StorageProvidersList.php | 11 +- .../Pages/Settings/Tags/Actions/EditTags.php | 104 ++++++---- composer.json | 1 - composer.lock | 73 +------ config/blade-icons.php | 183 ++++++++++++++++++ .../2024_10_06_091631_add_log_id_to_ssls.php | 22 +++ public/static/images/digital-ocean.svg | 7 - public/static/images/mysql.svg | 9 - public/static/images/nginx.svg | 7 - public/static/images/php-blank.svg | 37 ---- public/static/images/php.svg | 154 +++++++++++---- public/static/images/postgresql.svg | 10 - public/static/images/s3.svg | 1 - .../static/images => resources/svg}/aws.svg | 0 .../images => resources/svg}/bitbucket.svg | 0 resources/svg/cpu.svg | 23 +++ .../images => resources/svg}/custom.svg | 0 .../images => resources/svg}/digitalocean.svg | 0 .../images => resources/svg}/discord.svg | 0 resources/svg/disk.svg | 6 + .../images => resources/svg}/dropbox.svg | 0 .../static/images => resources/svg}/email.svg | 0 .../static/images => resources/svg}/ftp.svg | 0 .../images => resources/svg}/github.svg | 0 .../images => resources/svg}/gitlab.svg | 0 .../images => resources/svg}/google.svg | 0 .../images => resources/svg}/hetzner.svg | 0 .../images => resources/svg}/laravel.svg | 0 .../images => resources/svg}/linode.svg | 0 .../static/images => resources/svg}/local.svg | 0 .../static/images => resources/svg}/logo.png | Bin .../images => resources/svg}/mariadb.svg | 0 resources/svg/memory.svg | 7 + .../images => resources/svg}/monitoring.svg | 0 resources/svg/mysql.svg | 16 ++ resources/svg/nginx.svg | 12 ++ resources/svg/php-blank.svg | 5 + resources/svg/php.svg | 5 + .../images => resources/svg}/phpmyadmin.svg | 0 resources/svg/postgresql.svg | 21 ++ resources/svg/redis.svg | 37 ++++ .../svg}/remote-monitor.svg | 0 resources/svg/s3.svg | 2 + .../images => resources/svg}/server.svg | 0 .../static/images => resources/svg}/slack.svg | 0 resources/svg/supervisor.svg | 23 +++ .../images => resources/svg}/telegram.svg | 0 .../static/images => resources/svg}/ufw.svg | 0 .../images => resources/svg}/vito-agent.svg | 0 .../images => resources/svg}/vitomonitor.svg | 0 .../static/images => resources/svg}/vultr.svg | 0 .../images => resources/svg}/wasabi.svg | 0 .../images => resources/svg}/wordpress.svg | 0 .../static/images => resources/svg}/www.svg | 0 82 files changed, 1250 insertions(+), 345 deletions(-) create mode 100644 app/Actions/Queue/EditQueue.php create mode 100644 app/Policies/QueuePolicy.php create mode 100644 app/Policies/SslPolicy.php create mode 100644 app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php create mode 100644 app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php create mode 100644 config/blade-icons.php create mode 100644 database/migrations/2024_10_06_091631_add_log_id_to_ssls.php delete mode 100755 public/static/images/digital-ocean.svg delete mode 100644 public/static/images/mysql.svg delete mode 100644 public/static/images/nginx.svg delete mode 100644 public/static/images/php-blank.svg delete mode 100644 public/static/images/postgresql.svg delete mode 100644 public/static/images/s3.svg rename {public/static/images => resources/svg}/aws.svg (100%) rename {public/static/images => resources/svg}/bitbucket.svg (100%) create mode 100644 resources/svg/cpu.svg rename {public/static/images => resources/svg}/custom.svg (100%) rename {public/static/images => resources/svg}/digitalocean.svg (100%) mode change 100644 => 100755 rename {public/static/images => resources/svg}/discord.svg (100%) create mode 100644 resources/svg/disk.svg rename {public/static/images => resources/svg}/dropbox.svg (100%) rename {public/static/images => resources/svg}/email.svg (100%) rename {public/static/images => resources/svg}/ftp.svg (100%) rename {public/static/images => resources/svg}/github.svg (100%) rename {public/static/images => resources/svg}/gitlab.svg (100%) rename {public/static/images => resources/svg}/google.svg (100%) rename {public/static/images => resources/svg}/hetzner.svg (100%) rename {public/static/images => resources/svg}/laravel.svg (100%) rename {public/static/images => resources/svg}/linode.svg (100%) rename {public/static/images => resources/svg}/local.svg (100%) rename {public/static/images => resources/svg}/logo.png (100%) rename {public/static/images => resources/svg}/mariadb.svg (100%) create mode 100644 resources/svg/memory.svg rename {public/static/images => resources/svg}/monitoring.svg (100%) create mode 100644 resources/svg/mysql.svg create mode 100644 resources/svg/nginx.svg create mode 100644 resources/svg/php-blank.svg create mode 100644 resources/svg/php.svg rename {public/static/images => resources/svg}/phpmyadmin.svg (100%) create mode 100644 resources/svg/postgresql.svg create mode 100644 resources/svg/redis.svg rename {public/static/images => resources/svg}/remote-monitor.svg (100%) create mode 100755 resources/svg/s3.svg rename {public/static/images => resources/svg}/server.svg (100%) rename {public/static/images => resources/svg}/slack.svg (100%) create mode 100644 resources/svg/supervisor.svg rename {public/static/images => resources/svg}/telegram.svg (100%) rename {public/static/images => resources/svg}/ufw.svg (100%) rename {public/static/images => resources/svg}/vito-agent.svg (100%) rename {public/static/images => resources/svg}/vitomonitor.svg (100%) rename {public/static/images => resources/svg}/vultr.svg (100%) rename {public/static/images => resources/svg}/wasabi.svg (100%) rename {public/static/images => resources/svg}/wordpress.svg (100%) rename {public/static/images => resources/svg}/www.svg (100%) diff --git a/app/Actions/Queue/CreateQueue.php b/app/Actions/Queue/CreateQueue.php index 29809ba..68efde0 100644 --- a/app/Actions/Queue/CreateQueue.php +++ b/app/Actions/Queue/CreateQueue.php @@ -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(); } } diff --git a/app/Actions/Queue/DeleteQueue.php b/app/Actions/Queue/DeleteQueue.php index 03af4af..eb6a152 100644 --- a/app/Actions/Queue/DeleteQueue.php +++ b/app/Actions/Queue/DeleteQueue.php @@ -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(); } } diff --git a/app/Actions/Queue/EditQueue.php b/app/Actions/Queue/EditQueue.php new file mode 100644 index 0000000..a035e99 --- /dev/null +++ b/app/Actions/Queue/EditQueue.php @@ -0,0 +1,72 @@ +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', + ], + ]; + } +} diff --git a/app/Actions/SSL/CreateSSL.php b/app/Actions/SSL/CreateSSL.php index 427540f..d67e126 100644 --- a/app/Actions/SSL/CreateSSL.php +++ b/app/Actions/SSL/CreateSSL.php @@ -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; } } diff --git a/app/Actions/SSL/DeleteSSL.php b/app/Actions/SSL/DeleteSSL.php index 82e06e8..12a4caf 100644 --- a/app/Actions/SSL/DeleteSSL.php +++ b/app/Actions/SSL/DeleteSSL.php @@ -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(); } } diff --git a/app/Helpers/SSH.php b/app/Helpers/SSH.php index 4b872f7..6322894 100755 --- a/app/Helpers/SSH.php +++ b/app/Helpers/SSH.php @@ -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) { diff --git a/app/Models/Queue.php b/app/Models/Queue.php index 56aeb56..c98fd50 100644 --- a/app/Models/Queue.php +++ b/app/Models/Queue.php @@ -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) { diff --git a/app/Models/ServerLog.php b/app/Models/ServerLog.php index 33728d3..730357a 100755 --- a/app/Models/ServerLog.php +++ b/app/Models/ServerLog.php @@ -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 diff --git a/app/Models/Ssl.php b/app/Models/Ssl.php index 990f950..cb16017 100644 --- a/app/Models/Ssl.php +++ b/app/Models/Ssl.php @@ -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); + } } diff --git a/app/Policies/QueuePolicy.php b/app/Policies/QueuePolicy.php new file mode 100644 index 0000000..e9ceca2 --- /dev/null +++ b/app/Policies/QueuePolicy.php @@ -0,0 +1,55 @@ +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; + } +} diff --git a/app/Policies/ServicePolicy.php b/app/Policies/ServicePolicy.php index e4d7f8d..d14e896 100644 --- a/app/Policies/ServicePolicy.php +++ b/app/Policies/ServicePolicy.php @@ -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, - ]); - } } diff --git a/app/Policies/SslPolicy.php b/app/Policies/SslPolicy.php new file mode 100644 index 0000000..2e7c677 --- /dev/null +++ b/app/Policies/SslPolicy.php @@ -0,0 +1,55 @@ +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; + } +} diff --git a/app/SSH/Services/Webserver/Nginx.php b/app/SSH/Services/Webserver/Nginx.php index aca3000..f1e749b 100755 --- a/app/SSH/Services/Webserver/Nginx.php +++ b/app/SSH/Services/Webserver/Nginx.php @@ -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 diff --git a/app/Support/helpers.php b/app/Support/helpers.php index 884724b..735f92b 100755 --- a/app/Support/helpers.php +++ b/app/Support/helpers.php @@ -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')) { diff --git a/app/Web/Pages/Servers/CronJobs/Index.php b/app/Web/Pages/Servers/CronJobs/Index.php index 15e1fee..605ef50 100644 --- a/app/Web/Pages/Servers/CronJobs/Index.php +++ b/app/Web/Pages/Servers/CronJobs/Index.php @@ -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') diff --git a/app/Web/Pages/Servers/Index.php b/app/Web/Pages/Servers/Index.php index 3d1ed06..41f40d8 100644 --- a/app/Web/Pages/Servers/Index.php +++ b/app/Web/Pages/Servers/Index.php @@ -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(); - } + }); }), ]; } diff --git a/app/Web/Pages/Servers/Logs/Widgets/LogsList.php b/app/Web/Pages/Servers/Logs/Widgets/LogsList.php index ac52f16..5523fa1 100644 --- a/app/Web/Pages/Servers/Logs/Widgets/LogsList.php +++ b/app/Web/Pages/Servers/Logs/Widgets/LogsList.php @@ -26,6 +26,8 @@ class LogsList extends Widget public ?string $label = ''; + protected $listeners = ['$refresh']; + protected function getTableQuery(): Builder { return ServerLog::query() diff --git a/app/Web/Pages/Servers/Services/Widgets/ServicesList.php b/app/Web/Pages/Servers/Services/Widgets/ServicesList.php index 3219815..76438c7 100644 --- a/app/Web/Pages/Servers/Services/Widgets/ServicesList.php +++ b/app/Web/Pages/Servers/Services/Widgets/ServicesList.php @@ -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) { diff --git a/app/Web/Pages/Servers/Sites/Pages/Queues/Index.php b/app/Web/Pages/Servers/Sites/Pages/Queues/Index.php index e711b07..f6c9f5f 100644 --- a/app/Web/Pages/Servers/Sites/Pages/Queues/Index.php +++ b/app/Web/Pages/Servers/Sites/Pages/Queues/Index.php @@ -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'); + }); + }), + ]; } } diff --git a/app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php b/app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php new file mode 100644 index 0000000..23eb5cf --- /dev/null +++ b/app/Web/Pages/Servers/Sites/Pages/Queues/Widgets/QueuesList.php @@ -0,0 +1,160 @@ +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'); + }); + }); + } +} diff --git a/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php b/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php index b0a0af8..a9fe904 100644 --- a/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php +++ b/app/Web/Pages/Servers/Sites/Pages/SSL/Index.php @@ -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'); + }); + }), + ]; } } diff --git a/app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php b/app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php new file mode 100644 index 0000000..0da2e86 --- /dev/null +++ b/app/Web/Pages/Servers/Sites/Pages/SSL/Widgets/SslsList.php @@ -0,0 +1,81 @@ +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'); + }); + }), + ]); + } +} diff --git a/app/Web/Pages/Servers/Sites/Settings.php b/app/Web/Pages/Servers/Sites/Settings.php index f9ea36c..b6365f7 100644 --- a/app/Web/Pages/Servers/Sites/Settings.php +++ b/app/Web/Pages/Servers/Sites/Settings.php @@ -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([ diff --git a/app/Web/Pages/Servers/Sites/View.php b/app/Web/Pages/Servers/Sites/View.php index e00b5b0..5647e71 100644 --- a/app/Web/Pages/Servers/Sites/View.php +++ b/app/Web/Pages/Servers/Sites/View.php @@ -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') diff --git a/app/Web/Pages/Servers/Sites/Widgets/SitesList.php b/app/Web/Pages/Servers/Sites/Widgets/SitesList.php index 68d74d1..b94df30 100644 --- a/app/Web/Pages/Servers/Sites/Widgets/SitesList.php +++ b/app/Web/Pages/Servers/Sites/Widgets/SitesList.php @@ -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(), diff --git a/app/Web/Pages/Servers/Widgets/ServersList.php b/app/Web/Pages/Servers/Widgets/ServersList.php index e839f06..517b53a 100644 --- a/app/Web/Pages/Servers/Widgets/ServersList.php +++ b/app/Web/Pages/Servers/Widgets/ServersList.php @@ -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(), diff --git a/app/Web/Pages/Settings/ServerProviders/Widgets/ServerProvidersList.php b/app/Web/Pages/Settings/ServerProviders/Widgets/ServerProvidersList.php index 8f1cf4f..dc2b3cf 100644 --- a/app/Web/Pages/Settings/ServerProviders/Widgets/ServerProvidersList.php +++ b/app/Web/Pages/Settings/ServerProviders/Widgets/ServerProvidersList.php @@ -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') diff --git a/app/Web/Pages/Settings/SourceControls/Widgets/SourceControlsList.php b/app/Web/Pages/Settings/SourceControls/Widgets/SourceControlsList.php index 3c6e3ec..9126dcc 100644 --- a/app/Web/Pages/Settings/SourceControls/Widgets/SourceControlsList.php +++ b/app/Web/Pages/Settings/SourceControls/Widgets/SourceControlsList.php @@ -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') diff --git a/app/Web/Pages/Settings/StorageProviders/Widgets/StorageProvidersList.php b/app/Web/Pages/Settings/StorageProviders/Widgets/StorageProvidersList.php index a224ad6..7e6591d 100644 --- a/app/Web/Pages/Settings/StorageProviders/Widgets/StorageProvidersList.php +++ b/app/Web/Pages/Settings/StorageProviders/Widgets/StorageProvidersList.php @@ -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(), diff --git a/app/Web/Pages/Settings/Tags/Actions/EditTags.php b/app/Web/Pages/Settings/Tags/Actions/EditTags.php index b79600e..0357625 100644 --- a/app/Web/Pages/Settings/Tags/Actions/EditTags.php +++ b/app/Web/Pages/Settings/Tags/Actions/EditTags.php @@ -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(); + }; } } diff --git a/composer.json b/composer.json index 84d580a..1f1faac 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,6 @@ "php": "^8.2", "ext-ftp": "*", "aws/aws-sdk-php": "^3.158", - "codeat3/blade-simple-icons": "^5.10", "filament/filament": "^3.2", "laravel/fortify": "^1.17", "laravel/framework": "^11.0", diff --git a/composer.lock b/composer.lock index 28a05a1..6705c29 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "12a4dd2d7ef0bd63bdd94c2ab1ba72e1", + "content-hash": "616d8813a66d4a67234d11ebe6f90d67", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -557,77 +557,6 @@ ], "time": "2024-02-09T16:56:22+00:00" }, - { - "name": "codeat3/blade-simple-icons", - "version": "5.10.0", - "source": { - "type": "git", - "url": "https://github.com/codeat3/blade-simple-icons.git", - "reference": "2e6c78bca0200b008e23c60cd481ab750267ed9a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/codeat3/blade-simple-icons/zipball/2e6c78bca0200b008e23c60cd481ab750267ed9a", - "reference": "2e6c78bca0200b008e23c60cd481ab750267ed9a", - "shasum": "" - }, - "require": { - "blade-ui-kit/blade-icons": "^1.1", - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", - "php": "^7.4|^8.0" - }, - "require-dev": { - "codeat3/blade-icon-generation-helpers": "^0.8", - "codeat3/phpcs-styles": "^1.0", - "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", - "phpunit/phpunit": "^9.0|^10.5|^11.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Codeat3\\BladeSimpleIcons\\BladeSimpleIconsServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Codeat3\\BladeSimpleIcons\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Swapnil Sarwe", - "homepage": "https://swapnilsarwe.com" - }, - { - "name": "Dries Vints", - "homepage": "https://driesvints.com" - } - ], - "description": "A package to easily make use of \"Simple Icons\" in your Laravel Blade views. ", - "homepage": "https://github.com/codeat3/blade-simple-icons", - "keywords": [ - "blade", - "laravel", - "simpleicons" - ], - "support": { - "issues": "https://github.com/codeat3/blade-simple-icons/issues", - "source": "https://github.com/codeat3/blade-simple-icons/tree/5.10.0" - }, - "funding": [ - { - "url": "https://github.com/swapnilsarwe", - "type": "github" - } - ], - "time": "2024-09-21T14:06:07+00:00" - }, { "name": "danharrin/date-format-converter", "version": "v0.3.1", diff --git a/config/blade-icons.php b/config/blade-icons.php new file mode 100644 index 0000000..d026e51 --- /dev/null +++ b/config/blade-icons.php @@ -0,0 +1,183 @@ + [ + + 'default' => [ + + /* + |----------------------------------------------------------------- + | Icons Path + |----------------------------------------------------------------- + | + | Provide the relative path from your app root to your SVG icons + | directory. Icons are loaded recursively so there's no need to + | list every sub-directory. + | + | Relative to the disk root when the disk option is set. + | + */ + + 'path' => 'resources/svg', + + /* + |----------------------------------------------------------------- + | Filesystem Disk + |----------------------------------------------------------------- + | + | Optionally, provide a specific filesystem disk to read + | icons from. When defining a disk, the "path" option + | starts relatively from the disk root. + | + */ + + 'disk' => '', + + /* + |----------------------------------------------------------------- + | Default Prefix + |----------------------------------------------------------------- + | + | This config option allows you to define a default prefix for + | your icons. The dash separator will be applied automatically + | to every icon name. It's required and needs to be unique. + | + */ + + 'prefix' => 'icon', + + /* + |----------------------------------------------------------------- + | Fallback Icon + |----------------------------------------------------------------- + | + | This config option allows you to define a fallback + | icon when an icon in this set cannot be found. + | + */ + + 'fallback' => '', + + /* + |----------------------------------------------------------------- + | Default Set Classes + |----------------------------------------------------------------- + | + | This config option allows you to define some classes which + | will be applied by default to all icons within this set. + | + */ + + 'class' => '', + + /* + |----------------------------------------------------------------- + | Default Set Attributes + |----------------------------------------------------------------- + | + | This config option allows you to define some attributes which + | will be applied by default to all icons within this set. + | + */ + + 'attributes' => [ + // 'width' => 50, + // 'height' => 50, + ], + + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global Default Classes + |-------------------------------------------------------------------------- + | + | This config option allows you to define some classes which + | will be applied by default to all icons. + | + */ + + 'class' => '', + + /* + |-------------------------------------------------------------------------- + | Global Default Attributes + |-------------------------------------------------------------------------- + | + | This config option allows you to define some attributes which + | will be applied by default to all icons. + | + */ + + 'attributes' => [ + // 'width' => 50, + // 'height' => 50, + ], + + /* + |-------------------------------------------------------------------------- + | Global Fallback Icon + |-------------------------------------------------------------------------- + | + | This config option allows you to define a global fallback + | icon when an icon in any set cannot be found. It can + | reference any icon from any configured set. + | + */ + + 'fallback' => '', + + /* + |-------------------------------------------------------------------------- + | Components + |-------------------------------------------------------------------------- + | + | These config options allow you to define some + | settings related to Blade Components. + | + */ + + 'components' => [ + + /* + |---------------------------------------------------------------------- + | Disable Components + |---------------------------------------------------------------------- + | + | This config option allows you to disable Blade components + | completely. It's useful to avoid performance problems + | when working with large icon libraries. + | + */ + + 'disabled' => false, + + /* + |---------------------------------------------------------------------- + | Default Icon Component Name + |---------------------------------------------------------------------- + | + | This config option allows you to define the name + | for the default Icon class component. + | + */ + + 'default' => 'icon', + + ], + +]; diff --git a/database/migrations/2024_10_06_091631_add_log_id_to_ssls.php b/database/migrations/2024_10_06_091631_add_log_id_to_ssls.php new file mode 100644 index 0000000..f99bf4c --- /dev/null +++ b/database/migrations/2024_10_06_091631_add_log_id_to_ssls.php @@ -0,0 +1,22 @@ +unsignedInteger('log_id')->nullable(); + }); + } + + public function down(): void + { + Schema::table('ssls', function (Blueprint $table) { + $table->dropColumn('log_id'); + }); + } +}; diff --git a/public/static/images/digital-ocean.svg b/public/static/images/digital-ocean.svg deleted file mode 100755 index 083b8d9..0000000 --- a/public/static/images/digital-ocean.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/public/static/images/mysql.svg b/public/static/images/mysql.svg deleted file mode 100644 index 34358d2..0000000 --- a/public/static/images/mysql.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - MySQL - - - - - - diff --git a/public/static/images/nginx.svg b/public/static/images/nginx.svg deleted file mode 100644 index 38bc663..0000000 --- a/public/static/images/nginx.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/public/static/images/php-blank.svg b/public/static/images/php-blank.svg deleted file mode 100644 index bf57d7c..0000000 --- a/public/static/images/php-blank.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/static/images/php.svg b/public/static/images/php.svg index bf57d7c..4752a6f 100644 --- a/public/static/images/php.svg +++ b/public/static/images/php.svg @@ -1,37 +1,119 @@ - - - - - - - - - - - - - - - - - - - - - - + + + Official PHP Logo + + + + image/svg+xml + + Official PHP Logo + + + Colin Viebrock + + + + + + + + + + + + Copyright Colin Viebrock 1997 - All rights reserved. + + + 1997 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/postgresql.svg b/public/static/images/postgresql.svg deleted file mode 100644 index 0bdb3e3..0000000 --- a/public/static/images/postgresql.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/public/static/images/s3.svg b/public/static/images/s3.svg deleted file mode 100644 index 43ebabd..0000000 --- a/public/static/images/s3.svg +++ /dev/null @@ -1 +0,0 @@ -AWS Simple Storage Service (S3) \ No newline at end of file diff --git a/public/static/images/aws.svg b/resources/svg/aws.svg similarity index 100% rename from public/static/images/aws.svg rename to resources/svg/aws.svg diff --git a/public/static/images/bitbucket.svg b/resources/svg/bitbucket.svg similarity index 100% rename from public/static/images/bitbucket.svg rename to resources/svg/bitbucket.svg diff --git a/resources/svg/cpu.svg b/resources/svg/cpu.svg new file mode 100644 index 0000000..ac5e98a --- /dev/null +++ b/resources/svg/cpu.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/public/static/images/custom.svg b/resources/svg/custom.svg similarity index 100% rename from public/static/images/custom.svg rename to resources/svg/custom.svg diff --git a/public/static/images/digitalocean.svg b/resources/svg/digitalocean.svg old mode 100644 new mode 100755 similarity index 100% rename from public/static/images/digitalocean.svg rename to resources/svg/digitalocean.svg diff --git a/public/static/images/discord.svg b/resources/svg/discord.svg similarity index 100% rename from public/static/images/discord.svg rename to resources/svg/discord.svg diff --git a/resources/svg/disk.svg b/resources/svg/disk.svg new file mode 100644 index 0000000..984aa03 --- /dev/null +++ b/resources/svg/disk.svg @@ -0,0 +1,6 @@ + + + + diff --git a/public/static/images/dropbox.svg b/resources/svg/dropbox.svg similarity index 100% rename from public/static/images/dropbox.svg rename to resources/svg/dropbox.svg diff --git a/public/static/images/email.svg b/resources/svg/email.svg similarity index 100% rename from public/static/images/email.svg rename to resources/svg/email.svg diff --git a/public/static/images/ftp.svg b/resources/svg/ftp.svg similarity index 100% rename from public/static/images/ftp.svg rename to resources/svg/ftp.svg diff --git a/public/static/images/github.svg b/resources/svg/github.svg similarity index 100% rename from public/static/images/github.svg rename to resources/svg/github.svg diff --git a/public/static/images/gitlab.svg b/resources/svg/gitlab.svg similarity index 100% rename from public/static/images/gitlab.svg rename to resources/svg/gitlab.svg diff --git a/public/static/images/google.svg b/resources/svg/google.svg similarity index 100% rename from public/static/images/google.svg rename to resources/svg/google.svg diff --git a/public/static/images/hetzner.svg b/resources/svg/hetzner.svg similarity index 100% rename from public/static/images/hetzner.svg rename to resources/svg/hetzner.svg diff --git a/public/static/images/laravel.svg b/resources/svg/laravel.svg similarity index 100% rename from public/static/images/laravel.svg rename to resources/svg/laravel.svg diff --git a/public/static/images/linode.svg b/resources/svg/linode.svg similarity index 100% rename from public/static/images/linode.svg rename to resources/svg/linode.svg diff --git a/public/static/images/local.svg b/resources/svg/local.svg similarity index 100% rename from public/static/images/local.svg rename to resources/svg/local.svg diff --git a/public/static/images/logo.png b/resources/svg/logo.png similarity index 100% rename from public/static/images/logo.png rename to resources/svg/logo.png diff --git a/public/static/images/mariadb.svg b/resources/svg/mariadb.svg similarity index 100% rename from public/static/images/mariadb.svg rename to resources/svg/mariadb.svg diff --git a/resources/svg/memory.svg b/resources/svg/memory.svg new file mode 100644 index 0000000..d9e74ec --- /dev/null +++ b/resources/svg/memory.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/public/static/images/monitoring.svg b/resources/svg/monitoring.svg similarity index 100% rename from public/static/images/monitoring.svg rename to resources/svg/monitoring.svg diff --git a/resources/svg/mysql.svg b/resources/svg/mysql.svg new file mode 100644 index 0000000..1660f10 --- /dev/null +++ b/resources/svg/mysql.svg @@ -0,0 +1,16 @@ + + + MySQL + + + + + + diff --git a/resources/svg/nginx.svg b/resources/svg/nginx.svg new file mode 100644 index 0000000..3cd94d9 --- /dev/null +++ b/resources/svg/nginx.svg @@ -0,0 +1,12 @@ + + + + + + + diff --git a/resources/svg/php-blank.svg b/resources/svg/php-blank.svg new file mode 100644 index 0000000..6218c31 --- /dev/null +++ b/resources/svg/php-blank.svg @@ -0,0 +1,5 @@ + + + + diff --git a/resources/svg/php.svg b/resources/svg/php.svg new file mode 100644 index 0000000..6218c31 --- /dev/null +++ b/resources/svg/php.svg @@ -0,0 +1,5 @@ + + + + diff --git a/public/static/images/phpmyadmin.svg b/resources/svg/phpmyadmin.svg similarity index 100% rename from public/static/images/phpmyadmin.svg rename to resources/svg/phpmyadmin.svg diff --git a/resources/svg/postgresql.svg b/resources/svg/postgresql.svg new file mode 100644 index 0000000..9e9a370 --- /dev/null +++ b/resources/svg/postgresql.svg @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/resources/svg/redis.svg b/resources/svg/redis.svg new file mode 100644 index 0000000..2d29e3b --- /dev/null +++ b/resources/svg/redis.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/public/static/images/remote-monitor.svg b/resources/svg/remote-monitor.svg similarity index 100% rename from public/static/images/remote-monitor.svg rename to resources/svg/remote-monitor.svg diff --git a/resources/svg/s3.svg b/resources/svg/s3.svg new file mode 100755 index 0000000..ebd82ab --- /dev/null +++ b/resources/svg/s3.svg @@ -0,0 +1,2 @@ + + diff --git a/public/static/images/server.svg b/resources/svg/server.svg similarity index 100% rename from public/static/images/server.svg rename to resources/svg/server.svg diff --git a/public/static/images/slack.svg b/resources/svg/slack.svg similarity index 100% rename from public/static/images/slack.svg rename to resources/svg/slack.svg diff --git a/resources/svg/supervisor.svg b/resources/svg/supervisor.svg new file mode 100644 index 0000000..8c3d88d --- /dev/null +++ b/resources/svg/supervisor.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + diff --git a/public/static/images/telegram.svg b/resources/svg/telegram.svg similarity index 100% rename from public/static/images/telegram.svg rename to resources/svg/telegram.svg diff --git a/public/static/images/ufw.svg b/resources/svg/ufw.svg similarity index 100% rename from public/static/images/ufw.svg rename to resources/svg/ufw.svg diff --git a/public/static/images/vito-agent.svg b/resources/svg/vito-agent.svg similarity index 100% rename from public/static/images/vito-agent.svg rename to resources/svg/vito-agent.svg diff --git a/public/static/images/vitomonitor.svg b/resources/svg/vitomonitor.svg similarity index 100% rename from public/static/images/vitomonitor.svg rename to resources/svg/vitomonitor.svg diff --git a/public/static/images/vultr.svg b/resources/svg/vultr.svg similarity index 100% rename from public/static/images/vultr.svg rename to resources/svg/vultr.svg diff --git a/public/static/images/wasabi.svg b/resources/svg/wasabi.svg similarity index 100% rename from public/static/images/wasabi.svg rename to resources/svg/wasabi.svg diff --git a/public/static/images/wordpress.svg b/resources/svg/wordpress.svg similarity index 100% rename from public/static/images/wordpress.svg rename to resources/svg/wordpress.svg diff --git a/public/static/images/www.svg b/resources/svg/www.svg similarity index 100% rename from public/static/images/www.svg rename to resources/svg/www.svg