Add workers to servers (#547)

This commit is contained in:
Saeed Vaziry 2025-03-16 14:09:15 +01:00 committed by GitHub
parent 48ae561ea4
commit 72352aad8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 603 additions and 454 deletions

View File

@ -12,7 +12,7 @@ class SyncDatabaseUsers
public function sync(Server $server): void public function sync(Server $server): void
{ {
$service = $server->database(); $service = $server->database();
if (! $service) { if (! $service instanceof \App\Models\Service) {
return; return;
} }
/** @var Database $handler */ /** @var Database $handler */

View File

@ -12,7 +12,7 @@ class SyncDatabases
public function sync(Server $server): void public function sync(Server $server): void
{ {
$service = $server->database(); $service = $server->database();
if (! $service) { if (! $service instanceof \App\Models\Service) {
return; return;
} }
/** @var Database $handler */ /** @var Database $handler */

View File

@ -1,13 +0,0 @@
<?php
namespace App\Actions\Queue;
use App\Models\Queue;
class DeleteQueue
{
public function delete(Queue $queue): void
{
$queue->delete();
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Actions\Queue;
use App\Models\Queue;
use App\Models\Service;
use App\SSH\Services\ProcessManager\ProcessManager;
class GetQueueLogs
{
public function getLogs(Queue $queue): string
{
/** @var Service $service */
$service = $queue->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
return $handler->getLogs($queue->user, $queue->getLogFile());
}
}

View File

@ -1,56 +0,0 @@
<?php
namespace App\Actions\Queue;
use App\Enums\QueueStatus;
use App\Models\Queue;
use App\Models\Service;
use App\SSH\Services\ProcessManager\ProcessManager;
class ManageQueue
{
public function start(Queue $queue): void
{
$queue->status = QueueStatus::STARTING;
$queue->save();
dispatch(function () use ($queue): void {
/** @var Service $service */
$service = $queue->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->start($queue->id, $queue->site_id);
$queue->status = QueueStatus::RUNNING;
$queue->save();
})->onConnection('ssh');
}
public function stop(Queue $queue): void
{
$queue->status = QueueStatus::STOPPING;
$queue->save();
dispatch(function () use ($queue): void {
/** @var Service $service */
$service = $queue->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->stop($queue->id, $queue->site_id);
$queue->status = QueueStatus::STOPPED;
$queue->save();
})->onConnection('ssh');
}
public function restart(Queue $queue): void
{
$queue->status = QueueStatus::RESTARTING;
$queue->save();
dispatch(function () use ($queue): void {
/** @var Service $service */
$service = $queue->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->restart($queue->id, $queue->site_id);
$queue->status = QueueStatus::RUNNING;
$queue->save();
})->onConnection('ssh');
}
}

View File

@ -1,63 +1,63 @@
<?php <?php
namespace App\Actions\Queue; namespace App\Actions\Worker;
use App\Enums\QueueStatus; use App\Enums\WorkerStatus;
use App\Models\Queue;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use App\Models\Site;
use App\Models\Worker;
use App\SSH\Services\ProcessManager\ProcessManager; use App\SSH\Services\ProcessManager\ProcessManager;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class EditQueue class CreateWorker
{ {
/** /**
* @param array<string, mixed> $input * @param array<string, mixed> $input
* *
* @throws ValidationException * @throws ValidationException
*/ */
public function edit(Queue $queue, array $input): void public function create(Server $server, array $input, ?Site $site = null): void
{ {
$queue->fill([ $worker = new Worker([
'server_id' => $server->id,
'site_id' => $site?->id,
'command' => $input['command'], 'command' => $input['command'],
'user' => $input['user'], 'user' => $input['user'],
'auto_start' => $input['auto_start'] ? 1 : 0, 'auto_start' => $input['auto_start'] ? 1 : 0,
'auto_restart' => $input['auto_restart'] ? 1 : 0, 'auto_restart' => $input['auto_restart'] ? 1 : 0,
'numprocs' => $input['numprocs'], 'numprocs' => $input['numprocs'],
'status' => QueueStatus::RESTARTING, 'status' => WorkerStatus::CREATING,
]); ]);
$queue->save(); $worker->save();
dispatch(function () use ($queue): void { dispatch(function () use ($worker): void {
/** @var Service $service */ /** @var Service $service */
$service = $queue->server->processManager(); $service = $worker->server->processManager();
/** @var ProcessManager $processManager */ /** @var ProcessManager $processManager */
$processManager = $service->handler(); $processManager = $service->handler();
$processManager->delete($queue->id, $queue->site_id);
$processManager->create( $processManager->create(
$queue->id, $worker->id,
$queue->command, $worker->command,
$queue->user, $worker->user,
$queue->auto_start, $worker->auto_start,
$queue->auto_restart, $worker->auto_restart,
$queue->numprocs, $worker->numprocs,
$queue->getLogFile(), $worker->getLogFile(),
$queue->site_id $worker->site_id
); );
$queue->status = QueueStatus::RUNNING; $worker->status = WorkerStatus::RUNNING;
$queue->save(); $worker->save();
})->catch(function () use ($queue): void { })->catch(function () use ($worker): void {
$queue->status = QueueStatus::FAILED; $worker->delete();
$queue->save();
})->onConnection('ssh'); })->onConnection('ssh');
} }
/** /**
* @return array<string, array<string>> * @return array<string, array<string>>
*/ */
public static function rules(Server $server): array public static function rules(Server $server, ?Site $site = null): array
{ {
return [ return [
'command' => [ 'command' => [
@ -65,10 +65,7 @@ public static function rules(Server $server): array
], ],
'user' => [ 'user' => [
'required', 'required',
Rule::in([ Rule::in($site?->getSshUsers() ?? $server->getSshUsers()),
'root',
$server->ssh_user,
]),
], ],
'numprocs' => [ 'numprocs' => [
'required', 'required',

View File

@ -0,0 +1,13 @@
<?php
namespace App\Actions\Worker;
use App\Models\Worker;
class DeleteWorker
{
public function delete(Worker $worker): void
{
$worker->delete();
}
}

View File

@ -1,64 +1,64 @@
<?php <?php
namespace App\Actions\Queue; namespace App\Actions\Worker;
use App\Enums\QueueStatus; use App\Enums\WorkerStatus;
use App\Models\Queue;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use App\Models\Site; use App\Models\Site;
use App\Models\Worker;
use App\SSH\Services\ProcessManager\ProcessManager; use App\SSH\Services\ProcessManager\ProcessManager;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class CreateQueue class EditWorker
{ {
/** /**
* @param Server|Site $queueable
* @param array<string, mixed> $input * @param array<string, mixed> $input
* *
* @throws ValidationException * @throws ValidationException
*/ */
public function create(mixed $queueable, array $input): void public function edit(Worker $worker, array $input): void
{ {
$queue = new Queue([ $worker->fill([
'server_id' => $queueable instanceof Server ? $queueable->id : $queueable->server_id,
'site_id' => $queueable instanceof Site ? $queueable->id : null,
'command' => $input['command'], 'command' => $input['command'],
'user' => $input['user'], 'user' => $input['user'],
'auto_start' => $input['auto_start'] ? 1 : 0, 'auto_start' => $input['auto_start'] ? 1 : 0,
'auto_restart' => $input['auto_restart'] ? 1 : 0, 'auto_restart' => $input['auto_restart'] ? 1 : 0,
'numprocs' => $input['numprocs'], 'numprocs' => $input['numprocs'],
'status' => QueueStatus::CREATING, 'status' => WorkerStatus::RESTARTING,
]); ]);
$queue->save(); $worker->save();
dispatch(function () use ($queue): void { dispatch(function () use ($worker): void {
/** @var Service $service */ /** @var Service $service */
$service = $queue->server->processManager(); $service = $worker->server->processManager();
/** @var ProcessManager $processManager */ /** @var ProcessManager $processManager */
$processManager = $service->handler(); $processManager = $service->handler();
$processManager->delete($worker->id, $worker->site_id);
$processManager->create( $processManager->create(
$queue->id, $worker->id,
$queue->command, $worker->command,
$queue->user, $worker->user,
$queue->auto_start, $worker->auto_start,
$queue->auto_restart, $worker->auto_restart,
$queue->numprocs, $worker->numprocs,
$queue->getLogFile(), $worker->getLogFile(),
$queue->site_id $worker->site_id
); );
$queue->status = QueueStatus::RUNNING; $worker->status = WorkerStatus::RUNNING;
$queue->save(); $worker->save();
})->catch(function () use ($queue): void { })->catch(function () use ($worker): void {
$queue->delete(); $worker->status = WorkerStatus::FAILED;
$worker->save();
})->onConnection('ssh'); })->onConnection('ssh');
} }
/** /**
* @return array<string, array<string>> * @return array<string, array<string>>
*/ */
public static function rules(Site $site): array public static function rules(Server $server, ?Site $site = null): array
{ {
return [ return [
'command' => [ 'command' => [
@ -66,10 +66,7 @@ public static function rules(Site $site): array
], ],
'user' => [ 'user' => [
'required', 'required',
Rule::in([ Rule::in($site?->getSshUsers() ?? $server->getSshUsers()),
'root',
$site->user,
]),
], ],
'numprocs' => [ 'numprocs' => [
'required', 'required',

View File

@ -0,0 +1,21 @@
<?php
namespace App\Actions\Worker;
use App\Models\Service;
use App\Models\Worker;
use App\SSH\Services\ProcessManager\ProcessManager;
class GetWorkerLogs
{
public function getLogs(Worker $worker): string
{
/** @var Service $service */
$service = $worker->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
return $handler->getLogs($worker->user, $worker->getLogFile());
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace App\Actions\Worker;
use App\Enums\WorkerStatus;
use App\Models\Service;
use App\Models\Worker;
use App\SSH\Services\ProcessManager\ProcessManager;
class ManageWorker
{
public function start(Worker $worker): void
{
$worker->status = WorkerStatus::STARTING;
$worker->save();
dispatch(function () use ($worker): void {
/** @var Service $service */
$service = $worker->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->start($worker->id, $worker->site_id);
$worker->status = WorkerStatus::RUNNING;
$worker->save();
})->onConnection('ssh');
}
public function stop(Worker $worker): void
{
$worker->status = WorkerStatus::STOPPING;
$worker->save();
dispatch(function () use ($worker): void {
/** @var Service $service */
$service = $worker->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->stop($worker->id, $worker->site_id);
$worker->status = WorkerStatus::STOPPED;
$worker->save();
})->onConnection('ssh');
}
public function restart(Worker $worker): void
{
$worker->status = WorkerStatus::RESTARTING;
$worker->save();
dispatch(function () use ($worker): void {
/** @var Service $service */
$service = $worker->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->restart($worker->id, $worker->site_id);
$worker->status = WorkerStatus::RUNNING;
$worker->save();
})->onConnection('ssh');
}
}

View File

@ -37,7 +37,7 @@ public function handle(): void
$this->migrateModel(\App\Models\GitHook::class); $this->migrateModel(\App\Models\GitHook::class);
$this->migrateModel(\App\Models\NotificationChannel::class); $this->migrateModel(\App\Models\NotificationChannel::class);
$this->migrateModel(\App\Models\Project::class); $this->migrateModel(\App\Models\Project::class);
$this->migrateModel(\App\Models\Queue::class); $this->migrateModel(\App\Models\Worker::class);
$this->migrateModel(\App\Models\Server::class); $this->migrateModel(\App\Models\Server::class);
$this->migrateModel(\App\Models\ServerLog::class); $this->migrateModel(\App\Models\ServerLog::class);
$this->migrateModel(\App\Models\ServerProvider::class); $this->migrateModel(\App\Models\ServerProvider::class);

View File

@ -26,7 +26,5 @@ protected function schedule(Schedule $schedule): void
protected function commands(): void protected function commands(): void
{ {
$this->load(__DIR__.'/Commands'); $this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
} }
} }

View File

@ -10,7 +10,7 @@ final class SiteFeature
const SSL = 'ssl'; const SSL = 'ssl';
const QUEUES = 'queues'; const WORKERS = 'workers';
const COMMANDS = 'commands'; const COMMANDS = 'commands';
} }

View File

@ -2,7 +2,7 @@
namespace App\Enums; namespace App\Enums;
final class QueueStatus final class WorkerStatus
{ {
const RUNNING = 'running'; const RUNNING = 'running';

View File

@ -58,7 +58,7 @@
* @property Collection<int, DatabaseUser> $databaseUsers * @property Collection<int, DatabaseUser> $databaseUsers
* @property Collection<int, FirewallRule> $firewallRules * @property Collection<int, FirewallRule> $firewallRules
* @property Collection<int, CronJob> $cronJobs * @property Collection<int, CronJob> $cronJobs
* @property Collection<int, Queue> $queues * @property Collection<int, Worker> $queues
* @property Collection<int, Backup> $backups * @property Collection<int, Backup> $backups
* @property Collection<int, SshKey> $sshKeys * @property Collection<int, SshKey> $sshKeys
* @property Collection<int, Tag> $tags * @property Collection<int, Tag> $tags
@ -125,7 +125,7 @@ public static function boot(): void
try { try {
$server->sites()->each(function ($site): void { $server->sites()->each(function ($site): void {
/** @var Site $site */ /** @var Site $site */
$site->queues()->delete(); $site->workers()->delete();
$site->ssls()->delete(); $site->ssls()->delete();
$site->deployments()->delete(); $site->deployments()->delete();
$site->deploymentScript()->delete(); $site->deploymentScript()->delete();
@ -140,7 +140,7 @@ public static function boot(): void
$server->databaseUsers()->delete(); $server->databaseUsers()->delete();
$server->firewallRules()->delete(); $server->firewallRules()->delete();
$server->cronJobs()->delete(); $server->cronJobs()->delete();
$server->queues()->delete(); $server->workers()->delete();
$server->daemons()->delete(); $server->daemons()->delete();
$server->sshKeys()->detach(); $server->sshKeys()->detach();
if (File::exists($server->sshKey()['public_key_path'])) { if (File::exists($server->sshKey()['public_key_path'])) {
@ -265,11 +265,11 @@ public function cronJobs(): HasMany
} }
/** /**
* @return HasMany<Queue, covariant $this> * @return HasMany<Worker, covariant $this>
*/ */
public function queues(): HasMany public function workers(): HasMany
{ {
return $this->hasMany(Queue::class); return $this->hasMany(Worker::class);
} }
/** /**
@ -281,11 +281,11 @@ public function backups(): HasMany
} }
/** /**
* @return HasMany<Queue, covariant $this> * @return HasMany<Worker, covariant $this>
*/ */
public function daemons(): HasMany public function daemons(): HasMany
{ {
return $this->queues()->whereNull('site_id'); return $this->workers()->whereNull('site_id');
} }
/** /**

View File

@ -11,6 +11,7 @@
use App\SSH\Services\PHP\PHP; use App\SSH\Services\PHP\PHP;
use App\SSH\Services\Webserver\Webserver; use App\SSH\Services\Webserver\Webserver;
use App\Traits\HasProjectThroughServer; use App\Traits\HasProjectThroughServer;
use Database\Factories\SiteFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
@ -44,7 +45,7 @@
* @property Collection<int, Command> $commands * @property Collection<int, Command> $commands
* @property ?GitHook $gitHook * @property ?GitHook $gitHook
* @property ?DeploymentScript $deploymentScript * @property ?DeploymentScript $deploymentScript
* @property Collection<int, Queue> $queues * @property Collection<int, Worker> $workers
* @property Collection<int, Ssl> $ssls * @property Collection<int, Ssl> $ssls
* @property ?Ssl $activeSsl * @property ?Ssl $activeSsl
* @property string $ssh_key_name * @property string $ssh_key_name
@ -54,7 +55,7 @@
*/ */
class Site extends AbstractModel class Site extends AbstractModel
{ {
/** @use HasFactory<\Database\Factories\SiteFactory> */ /** @use HasFactory<SiteFactory> */
use HasFactory; use HasFactory;
use HasProjectThroughServer; use HasProjectThroughServer;
@ -105,9 +106,9 @@ public static function boot(): void
parent::boot(); parent::boot();
static::deleting(function (Site $site): void { static::deleting(function (Site $site): void {
$site->queues()->each(function ($queue): void { $site->workers()->each(function ($worker): void {
/** @var Queue $queue */ /** @var Worker $worker */
$queue->delete(); $worker->delete();
}); });
$site->ssls()->delete(); $site->ssls()->delete();
$site->deployments()->delete(); $site->deployments()->delete();
@ -186,11 +187,11 @@ public function deploymentScript(): HasOne
} }
/** /**
* @return HasMany<Queue, covariant $this> * @return HasMany<Worker, covariant $this>
*/ */
public function queues(): HasMany public function workers(): HasMany
{ {
return $this->hasMany(Queue::class); return $this->hasMany(Worker::class);
} }
/** /**
@ -401,4 +402,21 @@ public function loadBalancerServers(): HasMany
{ {
return $this->hasMany(LoadBalancerServer::class, 'load_balancer_id'); return $this->hasMany(LoadBalancerServer::class, 'load_balancer_id');
} }
/**
* @return array<string>
*/
public function getSshUsers(): array
{
$users = [
'root',
$this->server->getSshUser(),
];
if ($this->isIsolated()) {
$users[] = $this->user;
}
return $users;
}
} }

View File

@ -2,9 +2,9 @@
namespace App\Models; namespace App\Models;
use App\Enums\QueueStatus; use App\Enums\WorkerStatus;
use App\SSH\Services\ProcessManager\ProcessManager; use App\SSH\Services\ProcessManager\ProcessManager;
use Database\Factories\QueueFactory; use Database\Factories\WorkerFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -24,9 +24,9 @@
* @property Server $server * @property Server $server
* @property Site $site * @property Site $site
*/ */
class Queue extends AbstractModel class Worker extends AbstractModel
{ {
/** @use HasFactory<QueueFactory> */ /** @use HasFactory<WorkerFactory> */
use HasFactory; use HasFactory;
protected $fillable = [ protected $fillable = [
@ -55,28 +55,28 @@ class Queue extends AbstractModel
* @var array<string, string> * @var array<string, string>
*/ */
public static array $statusColors = [ public static array $statusColors = [
QueueStatus::RUNNING => 'success', WorkerStatus::RUNNING => 'success',
QueueStatus::CREATING => 'warning', WorkerStatus::CREATING => 'warning',
QueueStatus::DELETING => 'warning', WorkerStatus::DELETING => 'warning',
QueueStatus::FAILED => 'danger', WorkerStatus::FAILED => 'danger',
QueueStatus::STARTING => 'warning', WorkerStatus::STARTING => 'warning',
QueueStatus::STOPPING => 'warning', WorkerStatus::STOPPING => 'warning',
QueueStatus::RESTARTING => 'warning', WorkerStatus::RESTARTING => 'warning',
QueueStatus::STOPPED => 'gray', WorkerStatus::STOPPED => 'gray',
]; ];
public static function boot(): void public static function boot(): void
{ {
parent::boot(); parent::boot();
static::deleting(function (Queue $queue): void { static::deleting(function (Worker $worker): void {
try { try {
/** @var Service $service */ /** @var Service $service */
$service = $queue->server->processManager(); $service = $worker->server->processManager();
/** @var ProcessManager $handler */ /** @var ProcessManager $handler */
$handler = $service->handler(); $handler = $service->handler();
$handler->delete($queue->id, $queue->site_id); $handler->delete($worker->id, $worker->site_id);
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error($e); Log::error($e);
} }

View File

@ -1,61 +0,0 @@
<?php
namespace App\Policies;
use App\Enums\SiteFeature;
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->hasFeature(SiteFeature::QUEUES) &&
$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() &&
$site->hasFeature(SiteFeature::QUEUES) &&
$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->hasFeature(SiteFeature::QUEUES) &&
$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() &&
$site->hasFeature(SiteFeature::QUEUES) &&
$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() &&
$site->hasFeature(SiteFeature::QUEUES) &&
$queue->site_id === $site->id;
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace App\Policies;
use App\Enums\SiteFeature;
use App\Models\Server;
use App\Models\Site;
use App\Models\User;
use App\Models\Worker;
use Illuminate\Auth\Access\HandlesAuthorization;
class WorkerPolicy
{
use HandlesAuthorization;
public function viewAny(User $user, Server $server, ?Site $site = null): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$server->isReady() &&
(
! $site instanceof \App\Models\Site ||
(
$site->hasFeature(SiteFeature::WORKERS) &&
$site->isReady()
)
);
}
public function view(User $user, Worker $worker, Server $server, ?Site $site = null): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$site->server_id === $server->id &&
$server->isReady() &&
(
! $site instanceof \App\Models\Site ||
(
$site->hasFeature(SiteFeature::WORKERS) &&
$site->isReady() &&
$worker->site_id === $site->id
)
);
}
public function create(User $user, Server $server, ?Site $site = null): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$server->isReady() &&
(
! $site instanceof \App\Models\Site ||
(
$site->hasFeature(SiteFeature::WORKERS) &&
$site->isReady()
)
);
}
public function update(User $user, Worker $worker, Server $server, ?Site $site = null): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$site->server_id === $server->id &&
$server->isReady() &&
(
! $site instanceof \App\Models\Site ||
(
$site->hasFeature(SiteFeature::WORKERS) &&
$site->isReady() &&
$worker->site_id === $site->id
)
);
}
public function delete(User $user, Worker $worker, Server $server, ?Site $site = null): bool
{
return ($user->isAdmin() || $server->project->users->contains($user)) &&
$site->server_id === $server->id &&
$server->isReady() &&
(
! $site instanceof \App\Models\Site ||
(
$site->hasFeature(SiteFeature::WORKERS) &&
$site->isReady() &&
$worker->site_id === $site->id
)
);
}
}

View File

@ -286,7 +286,7 @@ public function getCharsets(): array
} }
} }
foreach ($results as $charset => $value) { foreach (array_keys($results) as $charset) {
$results[$charset]['list'] = $charsetCollations[$charset]; $results[$charset]['list'] = $charsetCollations[$charset];
} }
@ -310,9 +310,7 @@ public function getDatabases(): array
$databases = $this->tableToArray($data); $databases = $this->tableToArray($data);
return array_values(array_filter($databases, function ($database) { return array_values(array_filter($databases, fn ($database): bool => ! in_array($database[0], $this->systemDbs)));
return ! in_array($database[0], $this->systemDbs);
}));
} }
/** /**
@ -327,15 +325,11 @@ public function getUsers(): array
$users = $this->tableToArray($data); $users = $this->tableToArray($data);
$users = array_values(array_filter($users, function ($users) { $users = array_values(array_filter($users, fn ($users): bool => ! in_array($users[0], $this->systemUsers)));
return ! in_array($users[0], $this->systemUsers);
}));
foreach ($users as $key => $user) { foreach ($users as $key => $user) {
$databases = explode(',', $user[2]); $databases = explode(',', $user[2]);
$databases = array_values(array_filter($databases, function ($database) { $databases = array_values(array_filter($databases, fn ($database): bool => ! in_array($database, $this->systemDbs)));
return ! in_array($database, $this->systemDbs);
}));
$users[$key][2] = implode(',', $databases); $users[$key][2] = implode(',', $databases);
} }

View File

@ -27,9 +27,9 @@ public function deletionRules(): array
return [ return [
'service' => [ 'service' => [
function (string $attribute, mixed $value, Closure $fail): void { function (string $attribute, mixed $value, Closure $fail): void {
$hasQueue = $this->service->server->queues()->exists(); $hasWorker = $this->service->server->workers()->exists();
if ($hasQueue) { if ($hasWorker) {
$fail('You have queue(s) on the server.'); $fail('You have worker(s) on the server.');
} }
}, },
], ],

View File

@ -34,9 +34,9 @@ public function baseCommands(): array
'name' => 'Clear Application Cache Only', 'name' => 'Clear Application Cache Only',
'command' => 'php artisan cache:clear', 'command' => 'php artisan cache:clear',
], ],
// Queue Commands // Worker Commands
[ [
'name' => 'Restart Queue Workers', 'name' => 'Restart Workers',
'command' => 'php artisan queue:restart', 'command' => 'php artisan queue:restart',
], ],
[ [

View File

@ -15,7 +15,7 @@ public function supportedFeatures(): array
SiteFeature::COMMANDS, SiteFeature::COMMANDS,
SiteFeature::ENV, SiteFeature::ENV,
SiteFeature::SSL, SiteFeature::SSL,
SiteFeature::QUEUES, SiteFeature::WORKERS,
]; ];
} }

View File

@ -23,7 +23,7 @@ public function supportedFeatures(): array
SiteFeature::COMMANDS, SiteFeature::COMMANDS,
SiteFeature::ENV, SiteFeature::ENV,
SiteFeature::SSL, SiteFeature::SSL,
SiteFeature::QUEUES, SiteFeature::WORKERS,
]; ];
} }

View File

@ -94,8 +94,8 @@ protected function getHeaderActions(): array
->requiresConfirmation() ->requiresConfirmation()
->modalDescription('This will create databases that exist on the server but not in Vito.') ->modalDescription('This will create databases that exist on the server but not in Vito.')
->modalSubmitActionLabel('Sync') ->modalSubmitActionLabel('Sync')
->action(function () { ->action(function (): void {
run_action($this, function () { run_action($this, function (): void {
app(SyncDatabases::class)->sync($this->server); app(SyncDatabases::class)->sync($this->server);
$this->dispatch('$refresh'); $this->dispatch('$refresh');

View File

@ -38,8 +38,8 @@ protected function getHeaderActions(): array
->requiresConfirmation() ->requiresConfirmation()
->modalDescription('This will create db users that exist on the server but not in Vito.') ->modalDescription('This will create db users that exist on the server but not in Vito.')
->modalSubmitActionLabel('Sync') ->modalSubmitActionLabel('Sync')
->action(function () { ->action(function (): void {
run_action($this, function () { run_action($this, function (): void {
app(SyncDatabaseUsers::class)->sync($this->server); app(SyncDatabaseUsers::class)->sync($this->server);
$this->dispatch('$refresh'); $this->dispatch('$refresh');

View File

@ -12,6 +12,7 @@
use App\Models\Site; use App\Models\Site;
use App\Models\SshKey; use App\Models\SshKey;
use App\Models\User; use App\Models\User;
use App\Models\Worker;
use App\Web\Components\Page as BasePage; use App\Web\Components\Page as BasePage;
use App\Web\Pages\Servers\Console\Index as ConsoleIndex; use App\Web\Pages\Servers\Console\Index as ConsoleIndex;
use App\Web\Pages\Servers\CronJobs\Index as CronJobsIndex; use App\Web\Pages\Servers\CronJobs\Index as CronJobsIndex;
@ -28,6 +29,7 @@
use App\Web\Pages\Servers\SSHKeys\Index as SshKeysIndex; use App\Web\Pages\Servers\SSHKeys\Index as SshKeysIndex;
use App\Web\Pages\Servers\View as ServerView; use App\Web\Pages\Servers\View as ServerView;
use App\Web\Pages\Servers\Widgets\ServerSummary; use App\Web\Pages\Servers\Widgets\ServerSummary;
use App\Web\Pages\Servers\Workers\Index as WorkersIndex;
use Filament\Navigation\NavigationItem; use Filament\Navigation\NavigationItem;
abstract class Page extends BasePage abstract class Page extends BasePage
@ -99,6 +101,13 @@ public function getSubNavigation(): array
->url(CronJobsIndex::getUrl(parameters: ['server' => $this->server])); ->url(CronJobsIndex::getUrl(parameters: ['server' => $this->server]));
} }
if ($user->can('viewAny', [Worker::class, $this->server])) {
$items[] = NavigationItem::make(WorkersIndex::getNavigationLabel())
->icon('heroicon-o-queue-list')
->isActiveWhen(fn () => request()->routeIs(WorkersIndex::getRouteName().'*'))
->url(WorkersIndex::getUrl(parameters: ['server' => $this->server]));
}
if ($user->can('viewAnyServer', [SshKey::class, $this->server])) { if ($user->can('viewAnyServer', [SshKey::class, $this->server])) {
$items[] = NavigationItem::make(SshKeysIndex::getNavigationLabel()) $items[] = NavigationItem::make(SshKeysIndex::getNavigationLabel())
->icon('heroicon-o-key') ->icon('heroicon-o-key')

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,55 @@
<?php
namespace App\Web\Pages\Servers\Workers\Actions;
use App\Actions\Worker\CreateWorker;
use App\Models\Server;
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 Livewire\Component;
class Create
{
/**
* @return array<int, mixed>
*/
public static function form(Server $server, ?Site $site = null): array
{
return [
TextInput::make('command')
->rules(CreateWorker::rules($server, $site)['command'])
->helperText('Example: php /home/vito/your-site/artisan queue:work'),
Select::make('user')
->rules(fn (callable $get) => CreateWorker::rules($server, $site)['user'])
->options($server->getSshUsers()),
TextInput::make('numprocs')
->default(1)
->rules(CreateWorker::rules($server, $site)['numprocs'])
->helperText('Number of processes'),
Grid::make()
->schema([
Checkbox::make('auto_start')
->default(false),
Checkbox::make('auto_restart')
->default(false),
]),
];
}
/**
* @param array<string, mixed> $data
*/
public static function action(Component $component, array $data, Server $server, ?Site $site = null): void
{
app(CreateWorker::class)->create(
$server,
$data,
$site
);
$component->dispatch('$refresh');
}
}

View File

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

View File

@ -1,14 +1,15 @@
<?php <?php
namespace App\Web\Pages\Servers\Sites\Pages\Queues\Widgets; namespace App\Web\Pages\Servers\Workers\Widgets;
use App\Actions\Queue\DeleteQueue; use App\Actions\Worker\DeleteWorker;
use App\Actions\Queue\EditQueue; use App\Actions\Worker\EditWorker;
use App\Actions\Queue\GetQueueLogs; use App\Actions\Worker\GetWorkerLogs;
use App\Actions\Queue\ManageQueue; use App\Actions\Worker\ManageWorker;
use App\Models\Queue; use App\Models\Server;
use App\Models\Site; use App\Models\Site;
use App\Models\User; use App\Models\User;
use App\Models\Worker;
use Filament\Forms\Components\Checkbox; use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Grid; use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
@ -24,9 +25,11 @@
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\View\ComponentAttributeBag; use Illuminate\View\ComponentAttributeBag;
class QueuesList extends Widget class WorkersList extends Widget
{ {
public Site $site; public Server $server;
public ?Site $site = null;
/** /**
* @var array<string> * @var array<string>
@ -34,11 +37,17 @@ class QueuesList extends Widget
protected $listeners = ['$refresh']; protected $listeners = ['$refresh'];
/** /**
* @return Builder<Queue> * @return Builder<Worker>
*/ */
protected function getTableQuery(): Builder protected function getTableQuery(): Builder
{ {
return Queue::query()->where('site_id', $this->site->id); return Worker::query()
->where('server_id', $this->server->id)
->where(function (Builder $query): void {
if ($this->site instanceof \App\Models\Site) {
$query->where('site_id', $this->site->id);
}
});
} }
protected function getTableColumns(): array protected function getTableColumns(): array
@ -47,16 +56,16 @@ protected function getTableColumns(): array
TextColumn::make('command') TextColumn::make('command')
->limit(20) ->limit(20)
->copyable() ->copyable()
->tooltip(fn (Queue $record) => $record->command) ->tooltip(fn (Worker $record) => $record->command)
->searchable() ->searchable()
->sortable(), ->sortable(),
TextColumn::make('created_at') TextColumn::make('created_at')
->formatStateUsing(fn (Queue $record) => $record->created_at_by_timezone) ->formatStateUsing(fn (Worker $record) => $record->created_at_by_timezone)
->sortable(), ->sortable(),
TextColumn::make('status') TextColumn::make('status')
->label('Status') ->label('Status')
->badge() ->badge()
->color(fn (Queue $record) => Queue::$statusColors[$record->status]) ->color(fn (Worker $record) => Worker::$statusColors[$record->status])
->searchable() ->searchable()
->sortable(), ->sortable(),
]; ];
@ -86,12 +95,12 @@ private function operationAction(string $type, string $icon): Action
$user = auth()->user(); $user = auth()->user();
return Action::make($type) return Action::make($type)
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server])) ->authorize(fn (Worker $record) => $user->can('update', [$record, $this->server, $this->site]))
->label(ucfirst($type).' queue') ->label(ucfirst($type).' worker')
->icon($icon) ->icon($icon)
->action(function (Queue $record) use ($type): void { ->action(function (Worker $record) use ($type): void {
run_action($this, function () use ($record, $type): void { run_action($this, function () use ($record, $type): void {
app(ManageQueue::class)->$type($record); app(ManageWorker::class)->$type($record);
$this->dispatch('$refresh'); $this->dispatch('$refresh');
}); });
}); });
@ -104,10 +113,10 @@ private function logsAction(): Action
return Action::make('logs') return Action::make('logs')
->icon('heroicon-o-eye') ->icon('heroicon-o-eye')
->authorize(fn (Queue $record) => $user->can('view', [$record, $this->site, $this->site->server])) ->authorize(fn (Worker $record) => $user->can('view', [$record, $this->server, $this->site]))
->modalHeading('View Log') ->modalHeading('View Log')
->modalContent(fn (Queue $record) => view('components.console-view', [ ->modalContent(fn (Worker $record) => view('components.console-view', [
'slot' => app(GetQueueLogs::class)->getLogs($record), 'slot' => app(GetWorkerLogs::class)->getLogs($record),
'attributes' => new ComponentAttributeBag, 'attributes' => new ComponentAttributeBag,
])) ]))
->modalSubmitAction(false) ->modalSubmitAction(false)
@ -121,9 +130,9 @@ private function editAction(): Action
return EditAction::make('edit') return EditAction::make('edit')
->icon('heroicon-o-pencil-square') ->icon('heroicon-o-pencil-square')
->authorize(fn (Queue $record) => $user->can('update', [$record, $this->site, $this->site->server])) ->authorize(fn (Worker $record) => $user->can('update', [$record, $this->server, $this->site]))
->modalWidth(MaxWidth::ExtraLarge) ->modalWidth(MaxWidth::ExtraLarge)
->fillForm(fn (Queue $record): array => [ ->fillForm(fn (Worker $record): array => [
'command' => $record->command, 'command' => $record->command,
'user' => $record->user, 'user' => $record->user,
'numprocs' => $record->numprocs, 'numprocs' => $record->numprocs,
@ -132,17 +141,17 @@ private function editAction(): Action
]) ])
->form([ ->form([
TextInput::make('command') TextInput::make('command')
->rules(EditQueue::rules($this->site->server)['command']) ->rules(EditWorker::rules($this->server, $this->site)['command'])
->helperText('Example: php /home/vito/your-site/artisan queue:work'), ->helperText('Example: php /home/vito/your-site/artisan queue:work'),
Select::make('user') Select::make('user')
->rules(fn (callable $get) => EditQueue::rules($this->site->server)['user']) ->rules(fn (callable $get) => EditWorker::rules($this->server, $this->site)['user'])
->options([ ->options([
'vito' => $this->site->server->ssh_user, 'vito' => $this->server->ssh_user,
'root' => 'root', 'root' => 'root',
]), ]),
TextInput::make('numprocs') TextInput::make('numprocs')
->default(1) ->default(1)
->rules(EditQueue::rules($this->site->server)['numprocs']) ->rules(EditWorker::rules($this->server, $this->site)['numprocs'])
->helperText('Number of processes'), ->helperText('Number of processes'),
Grid::make() Grid::make()
->schema([ ->schema([
@ -152,9 +161,9 @@ private function editAction(): Action
->default(false), ->default(false),
]), ]),
]) ])
->using(function (Queue $record, array $data): void { ->using(function (Worker $record, array $data): void {
run_action($this, function () use ($record, $data): void { run_action($this, function () use ($record, $data): void {
app(EditQueue::class)->edit($record, $data); app(EditWorker::class)->edit($record, $data);
$this->dispatch('$refresh'); $this->dispatch('$refresh');
}); });
}); });
@ -167,10 +176,10 @@ private function deleteAction(): Action
return DeleteAction::make('delete') return DeleteAction::make('delete')
->icon('heroicon-o-trash') ->icon('heroicon-o-trash')
->authorize(fn (Queue $record) => $user->can('delete', [$record, $this->site, $this->site->server])) ->authorize(fn (Worker $record) => $user->can('delete', [$record, $this->server, $this->site]))
->using(function (Queue $record): void { ->using(function (Worker $record): void {
run_action($this, function () use ($record): void { run_action($this, function () use ($record): void {
app(DeleteQueue::class)->delete($record); app(DeleteWorker::class)->delete($record);
$this->dispatch('$refresh'); $this->dispatch('$refresh');
}); });
}); });

View File

@ -2,16 +2,16 @@
namespace Database\Factories; namespace Database\Factories;
use App\Enums\QueueStatus; use App\Enums\WorkerStatus;
use App\Models\Queue; use App\Models\Worker;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
/** /**
* @extends Factory<\App\Models\Queue> * @extends Factory<Worker>
*/ */
class QueueFactory extends Factory class WorkerFactory extends Factory
{ {
protected $model = Queue::class; protected $model = Worker::class;
public function definition(): array public function definition(): array
{ {
@ -23,7 +23,7 @@ public function definition(): array
'numprocs' => 1, 'numprocs' => 1,
'redirect_stderr' => 1, 'redirect_stderr' => 1,
'stdout_logfile' => 'file.log', 'stdout_logfile' => 'file.log',
'status' => QueueStatus::CREATING, 'status' => WorkerStatus::CREATING,
]; ];
} }
} }

View File

@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::rename('queues', 'workers');
Schema::table('workers', function (Blueprint $table): void {
$table->unsignedInteger('site_id')->nullable()->change();
});
}
public function down(): void
{
Schema::rename('workers', 'queues');
}
};

View File

@ -2,15 +2,15 @@
namespace Database\Seeders; namespace Database\Seeders;
use App\Enums\QueueStatus;
use App\Enums\SiteType; use App\Enums\SiteType;
use App\Enums\SslStatus; use App\Enums\SslStatus;
use App\Enums\SslType; use App\Enums\SslType;
use App\Models\Queue; use App\Enums\WorkerStatus;
use App\Models\Server; use App\Models\Server;
use App\Models\Site; use App\Models\Site;
use App\Models\SourceControl; use App\Models\SourceControl;
use App\Models\Ssl; use App\Models\Ssl;
use App\Models\Worker;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
@ -36,10 +36,10 @@ public function run(): void
'aliases' => ['www.'.$server->project->name.'.com'], 'aliases' => ['www.'.$server->project->name.'.com'],
]); ]);
$app->tags()->attach($server->tags()->first()); $app->tags()->attach($server->tags()->first());
Queue::factory()->create([ Worker::factory()->create([
'site_id' => $app->id, 'site_id' => $app->id,
'command' => 'php artisan queue:work', 'command' => 'php artisan queue:work',
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
Ssl::factory()->create([ Ssl::factory()->create([
'site_id' => $app->id, 'site_id' => $app->id,

View File

@ -1 +0,0 @@
<?php

View File

@ -1 +0,0 @@
<?php

View File

@ -1 +0,0 @@
<?php

View File

@ -2,25 +2,25 @@
namespace Tests\Feature; namespace Tests\Feature;
use App\Enums\QueueStatus; use App\Enums\WorkerStatus;
use App\Facades\SSH; use App\Facades\SSH;
use App\Models\Queue;
use App\Models\Site; use App\Models\Site;
use App\Web\Pages\Servers\Sites\Pages\Queues\Index; use App\Models\Worker;
use App\Web\Pages\Servers\Sites\Pages\Queues\Widgets\QueuesList; use App\Web\Pages\Servers\Sites\Pages\Workers\Index;
use App\Web\Pages\Servers\Sites\Pages\Workers\Widgets\WorkersList;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire; use Livewire\Livewire;
use Tests\TestCase; use Tests\TestCase;
class QueuesTest extends TestCase class WorkersTest extends TestCase
{ {
use RefreshDatabase; use RefreshDatabase;
public function test_see_queues() public function test_see_workers(): void
{ {
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
]); ]);
@ -32,33 +32,33 @@ public function test_see_queues()
]) ])
) )
->assertSuccessful() ->assertSuccessful()
->assertSee($queue->command); ->assertSee($worker->command);
} }
public function test_delete_queue() public function test_delete_worker(): void
{ {
SSH::fake(); SSH::fake();
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
]); ]);
Livewire::test(QueuesList::class, [ Livewire::test(WorkersList::class, [
'server' => $this->server, 'server' => $this->server,
'site' => $this->site, 'site' => $this->site,
]) ])
->callTableAction('delete', $queue->id) ->callTableAction('delete', $worker->id)
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseMissing('queues', [ $this->assertDatabaseMissing('workers', [
'id' => $queue->id, 'id' => $worker->id,
]); ]);
} }
public function test_create_queue() public function test_create_worker(): void
{ {
SSH::fake(); SSH::fake();
@ -69,7 +69,7 @@ public function test_create_queue()
'site' => $this->site, 'site' => $this->site,
]) ])
->callAction('create', [ ->callAction('create', [
'command' => 'php artisan queue:work', 'command' => 'php artisan worker:work',
'user' => 'vito', 'user' => 'vito',
'auto_start' => 1, 'auto_start' => 1,
'auto_restart' => 1, 'auto_restart' => 1,
@ -77,19 +77,19 @@ public function test_create_queue()
]) ])
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseHas('queues', [ $this->assertDatabaseHas('workers', [
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'command' => 'php artisan queue:work', 'command' => 'php artisan worker:work',
'user' => 'vito', 'user' => 'vito',
'auto_start' => 1, 'auto_start' => 1,
'auto_restart' => 1, 'auto_restart' => 1,
'numprocs' => 1, 'numprocs' => 1,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
} }
public function test_create_queue_as_isolated_user(): void public function test_create_worker_as_isolated_user(): void
{ {
SSH::fake(); SSH::fake();
@ -111,7 +111,7 @@ public function test_create_queue_as_isolated_user(): void
]) ])
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseHas('queues', [ $this->assertDatabaseHas('workers', [
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'command' => 'php artisan queue:work', 'command' => 'php artisan queue:work',
@ -119,11 +119,11 @@ public function test_create_queue_as_isolated_user(): void
'auto_start' => 1, 'auto_start' => 1,
'auto_restart' => 1, 'auto_restart' => 1,
'numprocs' => 1, 'numprocs' => 1,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
} }
public function test_cannot_create_queue_as_invalid_user(): void public function test_cannot_create_worker_as_invalid_user(): void
{ {
SSH::fake(); SSH::fake();
@ -142,14 +142,14 @@ public function test_cannot_create_queue_as_invalid_user(): void
]) ])
->assertHasActionErrors(); ->assertHasActionErrors();
$this->assertDatabaseMissing('queues', [ $this->assertDatabaseMissing('workers', [
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'user' => 'example', 'user' => 'example',
]); ]);
} }
public function test_cannot_create_queue_on_another_sites_user(): void public function test_cannot_create_worker_on_another_sites_user(): void
{ {
SSH::fake(); SSH::fake();
@ -173,85 +173,85 @@ public function test_cannot_create_queue_on_another_sites_user(): void
]) ])
->assertHasActionErrors(); ->assertHasActionErrors();
$this->assertDatabaseMissing('queues', [ $this->assertDatabaseMissing('workers', [
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'user' => 'example', 'user' => 'example',
]); ]);
} }
public function test_start_queue(): void public function test_start_worker(): void
{ {
SSH::fake(); SSH::fake();
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'status' => QueueStatus::STOPPED, 'status' => WorkerStatus::STOPPED,
]); ]);
Livewire::test(QueuesList::class, [ Livewire::test(WorkersList::class, [
'server' => $this->server, 'server' => $this->server,
'site' => $this->site, 'site' => $this->site,
]) ])
->callTableAction('start', $queue->id) ->callTableAction('start', $worker->id)
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseHas('queues', [ $this->assertDatabaseHas('workers', [
'id' => $queue->id, 'id' => $worker->id,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
} }
public function test_stop_queue(): void public function test_stop_worker(): void
{ {
SSH::fake(); SSH::fake();
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
Livewire::test(QueuesList::class, [ Livewire::test(WorkersList::class, [
'server' => $this->server, 'server' => $this->server,
'site' => $this->site, 'site' => $this->site,
]) ])
->callTableAction('stop', $queue->id) ->callTableAction('stop', $worker->id)
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseHas('queues', [ $this->assertDatabaseHas('workers', [
'id' => $queue->id, 'id' => $worker->id,
'status' => QueueStatus::STOPPED, 'status' => WorkerStatus::STOPPED,
]); ]);
} }
public function test_restart_queue(): void public function test_restart_worker(): void
{ {
SSH::fake(); SSH::fake();
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
Livewire::test(QueuesList::class, [ Livewire::test(WorkersList::class, [
'server' => $this->server, 'server' => $this->server,
'site' => $this->site, 'site' => $this->site,
]) ])
->callTableAction('restart', $queue->id) ->callTableAction('restart', $worker->id)
->assertSuccessful(); ->assertSuccessful();
$this->assertDatabaseHas('queues', [ $this->assertDatabaseHas('workers', [
'id' => $queue->id, 'id' => $worker->id,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
} }
@ -261,17 +261,17 @@ public function test_show_logs(): void
$this->actingAs($this->user); $this->actingAs($this->user);
$queue = Queue::factory()->create([ $worker = Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
'status' => QueueStatus::RUNNING, 'status' => WorkerStatus::RUNNING,
]); ]);
Livewire::test(QueuesList::class, [ Livewire::test(WorkersList::class, [
'server' => $this->server, 'server' => $this->server,
'site' => $this->site, 'site' => $this->site,
]) ])
->callTableAction('logs', $queue->id) ->callTableAction('logs', $worker->id)
->assertSuccessful(); ->assertSuccessful();
} }
} }

View File

@ -6,8 +6,8 @@
use App\Enums\ServiceStatus; use App\Enums\ServiceStatus;
use App\Facades\SSH; use App\Facades\SSH;
use App\Models\Database; use App\Models\Database;
use App\Models\Queue;
use App\Models\Service; use App\Models\Service;
use App\Models\Worker;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Tests\TestCase; use Tests\TestCase;
@ -74,7 +74,7 @@ public function test_cannot_uninstall_supervisor(): void
{ {
SSH::fake(); SSH::fake();
Queue::factory()->create([ Worker::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'site_id' => $this->site->id, 'site_id' => $this->site->id,
]); ]);