Add phpstan level 7(#544)

This commit is contained in:
Saeed Vaziry
2025-03-12 13:31:10 +01:00
committed by GitHub
parent c22bb1fa80
commit 493cbb0849
437 changed files with 4505 additions and 2193 deletions

View File

@ -23,6 +23,7 @@
*/
class Backup extends AbstractModel
{
/** @use HasFactory<\Database\Factories\BackupFactory> */
use HasFactory;
protected $fillable = [
@ -46,13 +47,18 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Backup $backup) {
$backup->files()->each(function (BackupFile $file) {
static::deleting(function ($backup): void {
/** @var Backup $backup */
$backup->files()->each(function ($file): void {
/** @var BackupFile $file */
$file->delete();
});
});
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
BackupStatus::RUNNING => 'success',
BackupStatus::FAILED => 'danger',
@ -62,31 +68,46 @@ public static function boot(): void
public function isCustomInterval(): bool
{
$intervals = array_keys(config('core.cronjob_intervals'));
$intervals = array_filter($intervals, fn ($interval) => $interval !== 'custom');
$intervals = array_filter($intervals, fn ($interval): bool => $interval !== 'custom');
return ! in_array($this->interval, $intervals);
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return BelongsTo<StorageProvider, covariant $this>
*/
public function storage(): BelongsTo
{
return $this->belongsTo(StorageProvider::class, 'storage_id');
}
/**
* @return BelongsTo<Database, covariant $this>
*/
public function database(): BelongsTo
{
return $this->belongsTo(Database::class)->withTrashed();
}
/**
* @return HasMany<BackupFile, covariant $this>
*/
public function files(): HasMany
{
return $this->hasMany(BackupFile::class, 'backup_id');
}
/**
* @return HasOne<BackupFile, covariant $this>
*/
public function lastFile(): HasOne
{
return $this->hasOne(BackupFile::class, 'backup_id')->latest();

View File

@ -20,6 +20,7 @@
*/
class BackupFile extends AbstractModel
{
/** @use HasFactory<\Database\Factories\BackupFileFactory> */
use HasFactory;
protected $fillable = [
@ -38,15 +39,16 @@ class BackupFile extends AbstractModel
protected static function booted(): void
{
static::created(function (BackupFile $backupFile) {
static::created(function (BackupFile $backupFile): void {
$keep = $backupFile->backup->keep_backups;
if ($backupFile->backup->files()->count() > $keep) {
/* @var BackupFile $lastFileToKeep */
/** @var ?BackupFile $lastFileToKeep */
$lastFileToKeep = $backupFile->backup->files()->orderByDesc('id')->skip($keep)->first();
if ($lastFileToKeep) {
$files = $backupFile->backup->files()
->where('id', '<=', $lastFileToKeep->id)
->get();
/** @var BackupFile $file */
foreach ($files as $file) {
app(ManageBackupFile::class)->delete($file);
}
@ -55,6 +57,9 @@ protected static function booted(): void
});
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
BackupFileStatus::CREATED => 'success',
BackupFileStatus::CREATING => 'warning',
@ -78,6 +83,9 @@ public function isLocal(): bool
return $this->backup->storage->provider === StorageProviderAlias::LOCAL;
}
/**
* @return BelongsTo<Backup, covariant $this>
*/
public function backup(): BelongsTo
{
return $this->belongsTo(Backup::class);
@ -96,7 +104,7 @@ public function path(): string
return match ($storage->provider) {
StorageProviderAlias::DROPBOX => '/'.$databaseName.'/'.$this->name.'.zip',
StorageProviderAlias::S3, StorageProviderAlias::FTP, StorageProviderAlias::LOCAL => implode('/', [
rtrim($storage->credentials['path'], '/'),
rtrim((string) $storage->credentials['path'], '/'),
$databaseName,
$this->name.'.zip',
]),

View File

@ -16,12 +16,13 @@
* @property string $command
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Collection<CommandExecution> $executions
* @property Collection<int, CommandExecution> $executions
* @property ?CommandExecution $lastExecution
* @property Site $site
*/
class Command extends AbstractModel
{
/** @use HasFactory<\Database\Factories\CommandFactory> */
use HasFactory;
protected $fillable = [
@ -38,32 +39,42 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Command $command) {
static::deleting(function (Command $command): void {
$command->executions()->delete();
});
}
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);
}
/**
* @return array<string>
*/
public function getVariables(): array
{
$variables = [];
preg_match_all('/\${(.*?)}/', $this->command, $matches);
foreach ($matches[1] as $match) {
$variables[] = $match;
}
$variables = $matches[1];
return array_unique($variables);
}
/**
* @return HasMany<CommandExecution, covariant $this>
*/
public function executions(): HasMany
{
return $this->hasMany(CommandExecution::class);
}
/**
* @return HasOne<CommandExecution, covariant $this>
*/
public function lastExecution(): HasOne
{
return $this->hasOne(CommandExecution::class)->latest();

View File

@ -13,7 +13,7 @@
* @property int $server_id
* @property int $user_id
* @property ?int $server_log_id
* @property array $variables
* @property array<mixed> $variables
* @property string $status
* @property Carbon $created_at
* @property Carbon $updated_at
@ -24,6 +24,7 @@
*/
class CommandExecution extends AbstractModel
{
/** @use HasFactory<\Database\Factories\CommandExecutionFactory> */
use HasFactory;
protected $fillable = [
@ -43,12 +44,18 @@ class CommandExecution extends AbstractModel
'variables' => 'array',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
CommandExecutionStatus::EXECUTING => 'warning',
CommandExecutionStatus::COMPLETED => 'success',
CommandExecutionStatus::FAILED => 'danger',
];
/**
* @return BelongsTo<Command, covariant $this>
*/
public function command(): BelongsTo
{
return $this->belongsTo(Command::class);
@ -58,7 +65,7 @@ public function getContent(): string
{
$content = $this->command->command;
foreach ($this->variables as $variable => $value) {
if (is_string($value) && ! empty($value)) {
if (is_string($value) && ($value !== '' && $value !== '0')) {
$content = str_replace('${'.$variable.'}', $value, $content);
}
}
@ -66,16 +73,25 @@ public function getContent(): string
return $content;
}
/**
* @return BelongsTo<ServerLog, covariant $this>
*/
public function serverLog(): BelongsTo
{
return $this->belongsTo(ServerLog::class);
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);

View File

@ -18,6 +18,7 @@
*/
class CronJob extends AbstractModel
{
/** @use HasFactory<\Database\Factories\CronJobFactory> */
use HasFactory;
protected $fillable = [
@ -34,6 +35,9 @@ class CronJob extends AbstractModel
'hidden' => 'boolean',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
CronjobStatus::CREATING => 'warning',
CronjobStatus::READY => 'success',
@ -43,6 +47,9 @@ class CronJob extends AbstractModel
CronjobStatus::DISABLED => 'gray',
];
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
@ -59,6 +66,7 @@ public static function crontab(Server $server, string $user): string
CronjobStatus::ENABLING,
])
->get();
/** @var CronJob $cronJob */
foreach ($cronJobs as $key => $cronJob) {
$data .= $cronJob->frequency.' '.$cronJob->command;
if ($key != count($cronJobs) - 1) {
@ -78,11 +86,8 @@ public function frequencyLabel(): string
'0 0 * * 0' => 'Weekly',
'0 0 1 * *' => 'Monthly',
];
if (isset($labels[$this->frequency])) {
return $labels[$this->frequency];
}
return $this->frequency;
return $labels[$this->frequency] ?? $this->frequency;
}
public function isEnabled(): bool

View File

@ -21,7 +21,9 @@
*/
class Database extends AbstractModel
{
/** @use HasFactory<\Database\Factories\DatabaseFactory> */
use HasFactory;
use SoftDeletes;
protected $fillable = [
@ -40,8 +42,9 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Database $database) {
$database->server->databaseUsers()->each(function (DatabaseUser $user) use ($database) {
static::deleting(function (Database $database): void {
$database->server->databaseUsers()->each(function ($user) use ($database): void {
/** @var DatabaseUser $user */
$databases = $user->databases;
if ($databases && in_array($database->name, $databases)) {
unset($databases[array_search($database->name, $databases)]);
@ -52,6 +55,9 @@ public static function boot(): void
});
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
DatabaseStatus::READY => 'success',
DatabaseStatus::CREATING => 'warning',
@ -59,11 +65,17 @@ public static function boot(): void
DatabaseStatus::FAILED => 'danger',
];
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return HasMany<Backup, covariant $this>
*/
public function backups(): HasMany
{
return $this->hasMany(Backup::class)->where('type', 'database');

View File

@ -10,13 +10,14 @@
* @property int $server_id
* @property string $username
* @property string $password
* @property array $databases
* @property array<string> $databases
* @property string $host
* @property string $status
* @property Server $server
*/
class DatabaseUser extends AbstractModel
{
/** @use HasFactory<\Database\Factories\DatabaseUserFactory> */
use HasFactory;
protected $fillable = [
@ -38,11 +39,17 @@ class DatabaseUser extends AbstractModel
'password',
];
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
DatabaseUserStatus::READY => 'success',
DatabaseUserStatus::CREATING => 'warning',

View File

@ -12,14 +12,15 @@
* @property int $log_id
* @property string $commit_id
* @property string $commit_id_short
* @property array $commit_data
* @property array<string, mixed> $commit_data
* @property string $status
* @property Site $site
* @property DeploymentScript $deploymentScript
* @property ServerLog $log
* @property ?ServerLog $log
*/
class Deployment extends AbstractModel
{
/** @use HasFactory<\Database\Factories\DeploymentFactory> */
use HasFactory;
protected $fillable = [
@ -38,22 +39,34 @@ class Deployment extends AbstractModel
'commit_data' => 'json',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
DeploymentStatus::DEPLOYING => 'warning',
DeploymentStatus::FINISHED => 'success',
DeploymentStatus::FAILED => 'danger',
];
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);
}
/**
* @return BelongsTo<DeploymentScript, covariant $this>
*/
public function deploymentScript(): BelongsTo
{
return $this->belongsTo(DeploymentScript::class);
}
/**
* @return BelongsTo<ServerLog, covariant $this>
*/
public function log(): BelongsTo
{
return $this->belongsTo(ServerLog::class, 'log_id');

View File

@ -13,13 +13,14 @@
*/
class DeploymentScript extends AbstractModel
{
/** @use HasFactory<\Database\Factories\DeploymentScriptFactory> */
use HasFactory;
protected static function boot(): void
{
parent::boot();
static::saving(function ($deploymentScript) {
static::saving(function ($deploymentScript): void {
$deploymentScript->content = str_replace("\r\n", "\n", $deploymentScript->content);
});
}
@ -34,6 +35,9 @@ protected static function boot(): void
'site_id' => 'integer',
];
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);

View File

@ -23,6 +23,7 @@
*/
class File extends AbstractModel
{
/** @use HasFactory<\Database\Factories\FileFactory> */
use HasFactory;
protected $fillable = [
@ -53,7 +54,7 @@ protected static function boot(): void
{
parent::boot();
static::deleting(function (File $file) {
static::deleting(function (File $file): bool {
if ($file->name === '.' || $file->name === '..') {
return false;
}
@ -64,11 +65,17 @@ protected static function boot(): void
});
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);

View File

@ -20,6 +20,7 @@
*/
class FirewallRule extends AbstractModel
{
/** @use HasFactory<\Database\Factories\FirewallRuleFactory> */
use HasFactory;
protected $fillable = [
@ -47,9 +48,13 @@ public function getStatusColor(): string
FirewallRuleStatus::DELETING => 'warning',
FirewallRuleStatus::READY => 'success',
FirewallRuleStatus::FAILED => 'danger',
default => 'secondary',
};
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);

View File

@ -10,15 +10,16 @@
* @property int $site_id
* @property int $source_control_id
* @property string $secret
* @property array $events
* @property array $actions
* @property array<string> $events
* @property array<string, mixed> $actions
* @property string $hook_id
* @property array $hook_response
* @property array<string, mixed> $hook_response
* @property Site $site
* @property SourceControl $sourceControl
*/
class GitHook extends AbstractModel
{
/** @use HasFactory<\Database\Factories\GitHookFactory> */
use HasFactory;
protected $fillable = [
@ -39,11 +40,17 @@ class GitHook extends AbstractModel
'hook_response' => 'json',
];
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);
}
/**
* @return BelongsTo<SourceControl, covariant $this>
*/
public function sourceControl(): BelongsTo
{
return $this->belongsTo(SourceControl::class);

View File

@ -15,6 +15,7 @@
*/
class LoadBalancerServer extends AbstractModel
{
/** @use HasFactory<\Database\Factories\LoadBalancerServerFactory> */
use HasFactory;
protected $fillable = [
@ -32,6 +33,9 @@ class LoadBalancerServer extends AbstractModel
'backup' => 'boolean',
];
/**
* @return BelongsTo<Site, covariant $this>
*/
public function loadBalancer(): BelongsTo
{
return $this->belongsTo(Site::class, 'load_balancer_id');
@ -39,6 +43,9 @@ public function loadBalancer(): BelongsTo
public function server(): ?Server
{
return $this->loadBalancer->project->servers()->where('local_ip', $this->ip)->first();
/** @var ?Server $server */
$server = $this->loadBalancer->project->servers()->where('local_ip', $this->ip)->first();
return $server;
}
}

View File

@ -10,13 +10,13 @@
/**
* @property int $id
* @property int $server_id
* @property float $load
* @property float $memory_total
* @property float $memory_used
* @property float $memory_free
* @property float $disk_total
* @property float $disk_used
* @property float $disk_free
* @property ?float $load
* @property ?float $memory_total
* @property ?float $memory_used
* @property ?float $memory_free
* @property ?float $disk_total
* @property ?float $disk_used
* @property ?float $disk_free
* @property-read float|int $memory_total_in_bytes
* @property-read float|int $memory_used_in_bytes
* @property-read float|int $memory_free_in_bytes
@ -29,6 +29,7 @@
*/
class Metric extends Model
{
/** @use HasFactory<\Database\Factories\MetricFactory> */
use HasFactory;
protected $fillable = [
@ -53,6 +54,9 @@ class Metric extends Model
'disk_free' => 'float',
];
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
@ -60,31 +64,31 @@ public function server(): BelongsTo
public function getMemoryTotalInBytesAttribute(): float|int
{
return $this->memory_total * 1024;
return ($this->memory_total ?? 0) * 1024;
}
public function getMemoryUsedInBytesAttribute(): float|int
{
return $this->memory_used * 1024;
return ($this->memory_used ?? 0) * 1024;
}
public function getMemoryFreeInBytesAttribute(): float|int
{
return $this->memory_free * 1024;
return ($this->memory_free ?? 0) * 1024;
}
public function getDiskTotalInBytesAttribute(): float|int
{
return $this->disk_total * (1024 * 1024);
return ($this->disk_total ?? 0) * (1024 * 1024);
}
public function getDiskUsedInBytesAttribute(): float|int
{
return $this->disk_used * (1024 * 1024);
return ($this->disk_used ?? 0) * (1024 * 1024);
}
public function getDiskFreeInBytesAttribute(): float|int
{
return $this->disk_free * (1024 * 1024);
return ($this->disk_free ?? 0) * (1024 * 1024);
}
}

View File

@ -10,15 +10,17 @@
/**
* @property int $id
* @property string provider
* @property array data
* @property string label
* @property bool connected
* @property string $provider
* @property array<string, mixed> $data
* @property string $label
* @property bool $connected
* @property int $project_id
*/
class NotificationChannel extends AbstractModel
{
/** @use HasFactory<\Database\Factories\NotificationChannelFactory> */
use HasFactory;
use Notifiable;
protected $fillable = [
@ -41,7 +43,10 @@ public function provider(): \App\NotificationChannels\NotificationChannel
{
$class = config('core.notification_channels_providers_class')[$this->provider];
return new $class($this);
/** @var \App\NotificationChannels\NotificationChannel $provider */
$provider = new $class($this);
return $provider;
}
public static function notifyAll(NotificationInterface $notification): void
@ -52,16 +57,24 @@ public static function notifyAll(NotificationInterface $notification): void
}
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return Builder<NotificationChannel>
*/
public static function getByProjectId(int $projectId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId) {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
/** @var Builder<NotificationChannel> $query */
$query = NotificationChannel::query();
return $query->where(function (Builder $query) use ($projectId): void {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
}
}

View File

@ -12,7 +12,7 @@
* @property int $tokenable_id
* @property string $name
* @property string $token
* @property array $abilities
* @property array<string> $abilities
* @property Carbon $last_used_at
* @property Carbon $created_at
* @property Carbon $updated_at

View File

@ -18,14 +18,16 @@
* @property Carbon $created_at
* @property Carbon $updated_at
* @property User $user
* @property Collection<Server> $servers
* @property Collection<User> $users
* @property Collection<NotificationChannel> $notificationChannels
* @property Collection<SourceControl> $sourceControls
* @property Collection<int, Server> $servers
* @property Collection<int, User> $users
* @property Collection<int, NotificationChannel> $notificationChannels
* @property Collection<int, SourceControl> $sourceControls
*/
class Project extends Model
{
/** @use HasFactory<\Database\Factories\ProjectFactory> */
use HasFactory;
use HasTimezoneTimestamps;
protected $fillable = [
@ -37,38 +39,57 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Project $project) {
$project->servers()->each(function (Server $server) {
static::deleting(function (Project $project): void {
$project->servers()->each(function ($server): void {
/** @var Server $server */
$server->delete();
});
});
}
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* @return HasMany<Server, covariant $this>
*/
public function servers(): HasMany
{
return $this->hasMany(Server::class);
}
/**
* @return HasMany<NotificationChannel, covariant $this>
*/
public function notificationChannels(): HasMany
{
return $this->hasMany(NotificationChannel::class);
}
/**
* @return BelongsToMany<User, covariant $this>
*/
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class, 'user_project')->withTimestamps();
}
/**
* @return HasMany<SourceControl, covariant $this>
*/
public function sourceControls(): HasMany
{
return $this->hasMany(SourceControl::class);
}
/**
* @return HasMany<Tag, covariant $this>
*/
public function tags(): HasMany
{
return $this->hasMany(Tag::class);

View File

@ -3,6 +3,8 @@
namespace App\Models;
use App\Enums\QueueStatus;
use App\SSH\Services\ProcessManager\ProcessManager;
use Database\Factories\QueueFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Log;
@ -24,6 +26,7 @@
*/
class Queue extends AbstractModel
{
/** @use HasFactory<QueueFactory> */
use HasFactory;
protected $fillable = [
@ -48,6 +51,9 @@ class Queue extends AbstractModel
'redirect_stderr' => 'boolean',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
QueueStatus::RUNNING => 'success',
QueueStatus::CREATING => 'warning',
@ -63,9 +69,14 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Queue $queue) {
static::deleting(function (Queue $queue): void {
try {
$queue->server->processManager()->handler()->delete($queue->id, $queue->site_id);
/** @var Service $service */
$service = $queue->server->processManager();
/** @var ProcessManager $handler */
$handler = $service->handler();
$handler->delete($queue->id, $queue->site_id);
} catch (Throwable $e) {
Log::error($e);
}
@ -74,7 +85,7 @@ public static function boot(): void
public function getServerIdAttribute(int $value): int
{
if (! $value) {
if ($value === 0) {
$value = $this->site->server_id;
$this->fill(['server_id' => $this->site->server_id]);
$this->save();
@ -83,11 +94,17 @@ public function getServerIdAttribute(int $value): int
return $value;
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);

View File

@ -17,14 +17,15 @@
* @property string $content
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Collection<ScriptExecution> $executions
* @property Collection<int, ScriptExecution> $executions
* @property ?ScriptExecution $lastExecution
* @property User $user
* @property int $project_id
* @property ?int $project_id
* @property ?Project $project
*/
class Script extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ScriptFactory> */
use HasFactory;
protected $fillable = [
@ -43,46 +44,65 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Script $script) {
static::deleting(function (Script $script): void {
$script->executions()->delete();
});
}
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return array<string>
*/
public function getVariables(): array
{
$variables = [];
preg_match_all('/\${(.*?)}/', $this->content, $matches);
foreach ($matches[1] as $match) {
$variables[] = $match;
}
$variables = $matches[1];
return array_unique($variables);
}
/**
* @return HasMany<ScriptExecution, covariant $this>
*/
public function executions(): HasMany
{
return $this->hasMany(ScriptExecution::class);
}
/**
* @return HasOne<ScriptExecution, covariant $this>
*/
public function lastExecution(): HasOne
{
return $this->hasOne(ScriptExecution::class)->latest();
}
/**
* @return Builder<Script>
*/
public static function getByProjectId(int $projectId, int $userId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId, $userId) {
/** @var Builder<Script> $query */
$query = static::query();
return $query
->where(function (Builder $query) use ($projectId, $userId): void {
$query->where('project_id', $projectId)
->orWhere('user_id', $userId)
->orWhereNull('project_id');

View File

@ -13,7 +13,7 @@
* @property int $server_log_id
* @property ?int $server_id
* @property string $user
* @property array $variables
* @property array<mixed> $variables
* @property string $status
* @property Carbon $created_at
* @property Carbon $updated_at
@ -23,6 +23,7 @@
*/
class ScriptExecution extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ScriptExecutionFactory> */
use HasFactory;
protected $fillable = [
@ -41,12 +42,18 @@ class ScriptExecution extends AbstractModel
'variables' => 'array',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
ScriptExecutionStatus::EXECUTING => 'warning',
ScriptExecutionStatus::COMPLETED => 'success',
ScriptExecutionStatus::FAILED => 'danger',
];
/**
* @return BelongsTo<Script, covariant $this>
*/
public function script(): BelongsTo
{
return $this->belongsTo(Script::class);
@ -56,7 +63,7 @@ public function getContent(): string
{
$content = $this->script->content;
foreach ($this->variables as $variable => $value) {
if (is_string($value) && ! empty($value)) {
if (is_string($value) && ($value !== '' && $value !== '0')) {
$content = str_replace('${'.$variable.'}', $value, $content);
}
}
@ -64,11 +71,17 @@ public function getContent(): string
return $content;
}
/**
* @return BelongsTo<ServerLog, covariant $this>
*/
public function serverLog(): BelongsTo
{
return $this->belongsTo(ServerLog::class);
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);

View File

@ -19,6 +19,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
@ -31,43 +32,43 @@
* @property string $name
* @property string $ssh_user
* @property string $ip
* @property string $local_ip
* @property ?string $local_ip
* @property int $port
* @property string $os
* @property string $type
* @property array $type_data
* @property array<string, mixed> $type_data
* @property string $provider
* @property int $provider_id
* @property array $provider_data
* @property array $authentication
* @property array<string, mixed> $provider_data
* @property array<string, mixed> $authentication
* @property string $public_key
* @property string $status
* @property bool $auto_update
* @property int $available_updates
* @property int $security_updates
* @property int $progress
* @property string $progress_step
* @property int|float $progress
* @property ?string $progress_step
* @property Project $project
* @property User $creator
* @property ServerProvider $serverProvider
* @property ServerLog[] $logs
* @property Site[] $sites
* @property Service[] $services
* @property Database[] $databases
* @property DatabaseUser[] $databaseUsers
* @property FirewallRule[] $firewallRules
* @property CronJob[] $cronJobs
* @property Queue[] $queues
* @property Backup[] $backups
* @property Queue[] $daemons
* @property SshKey[] $sshKeys
* @property Tag[] $tags
* @property Collection<int, ServerLog> $logs
* @property Collection<int, Site> $sites
* @property Collection<int, Service> $services
* @property Collection<int, Database> $databases
* @property Collection<int, DatabaseUser> $databaseUsers
* @property Collection<int, FirewallRule> $firewallRules
* @property Collection<int, CronJob> $cronJobs
* @property Collection<int, Queue> $queues
* @property Collection<int, Backup> $backups
* @property Collection<int, SshKey> $sshKeys
* @property Collection<int, Tag> $tags
* @property string $hostname
* @property int $updates
* @property Carbon $last_update_check
* @property ?Carbon $last_update_check
*/
class Server extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServerFactory> */
use HasFactory;
protected $fillable = [
@ -106,7 +107,7 @@ class Server extends AbstractModel
'auto_update' => 'boolean',
'available_updates' => 'integer',
'security_updates' => 'integer',
'progress' => 'integer',
'progress' => 'float',
'updates' => 'integer',
'last_update_check' => 'datetime',
];
@ -119,17 +120,19 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Server $server) {
static::deleting(function (Server $server): void {
DB::beginTransaction();
try {
$server->sites()->each(function (Site $site) {
$server->sites()->each(function ($site): void {
/** @var Site $site */
$site->queues()->delete();
$site->ssls()->delete();
$site->deployments()->delete();
$site->deploymentScript()->delete();
});
$server->sites()->delete();
$server->logs()->each(function (ServerLog $log) {
$server->logs()->each(function ($log): void {
/** @var ServerLog $log */
$log->delete();
});
$server->services()->delete();
@ -155,6 +158,9 @@ public static function boot(): void
});
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
ServerStatus::READY => 'success',
ServerStatus::INSTALLING => 'warning',
@ -178,76 +184,121 @@ public function isInstallationFailed(): bool
return $this->status === ServerStatus::INSTALLATION_FAILED;
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class, 'project_id');
}
/**
* @return BelongsTo<User, covariant $this>
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* @return BelongsTo<ServerProvider, covariant $this>
*/
public function serverProvider(): BelongsTo
{
return $this->belongsTo(ServerProvider::class, 'provider_id');
}
/**
* @return HasMany<ServerLog, covariant $this>
*/
public function logs(): HasMany
{
return $this->hasMany(ServerLog::class);
}
/**
* @return HasMany<Site, covariant $this>
*/
public function sites(): HasMany
{
return $this->hasMany(Site::class);
}
/**
* @return HasMany<Service, covariant $this>
*/
public function services(): HasMany
{
return $this->hasMany(Service::class);
}
/**
* @return HasMany<Database, covariant $this>
*/
public function databases(): HasMany
{
return $this->hasMany(Database::class);
}
/**
* @return HasMany<DatabaseUser, covariant $this>
*/
public function databaseUsers(): HasMany
{
return $this->hasMany(DatabaseUser::class);
}
/**
* @return HasMany<FirewallRule, covariant $this>
*/
public function firewallRules(): HasMany
{
return $this->hasMany(FirewallRule::class);
}
/**
* @return HasMany<CronJob, covariant $this>
*/
public function cronJobs(): HasMany
{
return $this->hasMany(CronJob::class);
}
/**
* @return HasMany<Queue, covariant $this>
*/
public function queues(): HasMany
{
return $this->hasMany(Queue::class);
}
/**
* @return HasMany<Backup, covariant $this>
*/
public function backups(): HasMany
{
return $this->hasMany(Backup::class);
}
/**
* @return HasMany<Queue, covariant $this>
*/
public function daemons(): HasMany
{
return $this->queues()->whereNull('site_id');
}
/**
* @return HasMany<Metric, covariant $this>
*/
public function metrics(): HasMany
{
return $this->hasMany(Metric::class);
}
/**
* @return BelongsToMany<SshKey, covariant $this>
*/
public function sshKeys(): BelongsToMany
{
return $this->belongsToMany(SshKey::class, 'server_ssh_keys')
@ -255,6 +306,9 @@ public function sshKeys(): BelongsToMany
->withTimestamps();
}
/**
* @return MorphToMany<Tag, covariant $this>
*/
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
@ -269,6 +323,9 @@ public function getSshUser(): string
return config('core.ssh_user');
}
/**
* @return array<string>
*/
public function getSshUsers(): array
{
$users = ['root', $this->getSshUser()];
@ -278,11 +335,11 @@ public function getSshUsers(): array
return array_unique($users);
}
public function service($type, $version = null): ?Service
public function service(string $type, mixed $version = null): ?Service
{
/* @var Service $service */
/** @var ?Service $service */
$service = $this->services()
->where(function ($query) use ($type, $version) {
->where(function ($query) use ($type, $version): void {
$query->where('type', $type);
if ($version) {
$query->where('version', $version);
@ -293,9 +350,9 @@ public function service($type, $version = null): ?Service
return $service;
}
public function defaultService($type): ?Service
public function defaultService(string $type): ?Service
{
/* @var Service $service */
/** @var ?Service $service */
$service = $this->services()
->where('type', $type)
->where('is_default', 1)
@ -303,13 +360,13 @@ public function defaultService($type): ?Service
// If no default service found, get the first service with status ready or stopped
if (! $service) {
/** @var Service $service */
/** @var ?Service $service */
$service = $this->services()
->where('type', $type)
->whereIn('status', [ServiceStatus::READY, ServiceStatus::STOPPED])
->first();
if ($service) {
$service->is_default = 1;
$service->is_default = true;
$service->save();
}
}
@ -322,10 +379,14 @@ public function ssh(?string $user = null): \App\Helpers\SSH|SSHFake
return SSH::init($this, $user);
}
/**
* @return array<int, string>
*/
public function installedPHPVersions(): array
{
$versions = [];
$phps = $this->services()->where('type', 'php')->get(['version']);
/** @var Service $php */
foreach ($phps as $php) {
$versions[] = $php->version;
}
@ -333,10 +394,14 @@ public function installedPHPVersions(): array
return $versions;
}
/**
* @return array<int, string>
*/
public function installedNodejsVersions(): array
{
$versions = [];
$nodes = $this->services()->where('type', 'nodejs')->get(['version']);
/** @var Service $node */
foreach ($nodes as $node) {
$versions[] = $node->version;
}
@ -348,19 +413,25 @@ public function type(): ServerType
{
$typeClass = config('core.server_types_class')[$this->type];
return new $typeClass($this);
/** @var ServerType $type */
$type = new $typeClass($this);
return $type;
}
public function provider(): \App\ServerProviders\ServerProvider
{
$providerClass = config('core.server_providers_class')[$this->provider];
return new $providerClass($this->serverProvider, $this);
/** @var \App\ServerProviders\ServerProvider $provider */
$provider = new $providerClass($this->serverProvider ?? new ServerProvider, $this);
return $provider;
}
public function webserver(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('webserver');
}
@ -369,7 +440,7 @@ public function webserver(?string $version = null): ?Service
public function database(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('database');
}
@ -378,7 +449,7 @@ public function database(?string $version = null): ?Service
public function firewall(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('firewall');
}
@ -387,7 +458,7 @@ public function firewall(?string $version = null): ?Service
public function processManager(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('process_manager');
}
@ -396,7 +467,7 @@ public function processManager(?string $version = null): ?Service
public function php(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('php');
}
@ -405,7 +476,7 @@ public function php(?string $version = null): ?Service
public function nodejs(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('nodejs');
}
@ -414,7 +485,7 @@ public function nodejs(?string $version = null): ?Service
public function memoryDatabase(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('memory_database');
}
@ -423,20 +494,23 @@ public function memoryDatabase(?string $version = null): ?Service
public function monitoring(?string $version = null): ?Service
{
if (! $version) {
if ($version === null || $version === '' || $version === '0') {
return $this->defaultService('monitoring');
}
return $this->service('monitoring', $version);
}
/**
* @return array<string, string>
*/
public function sshKey(): array
{
/** @var FilesystemAdapter $storageDisk */
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
return [
'public_key' => Str::replace("\n", '', Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub')),
'public_key' => str(Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub'))->replace("\n", '')->toString(),
'public_key_path' => $storageDisk->path($this->id.'.pub'),
'private_key_path' => $storageDisk->path((string) $this->id),
];
@ -479,7 +553,7 @@ public function checkForUpdates(): void
public function getAvailableUpdatesAttribute(?int $value): int
{
if (! $value) {
if ($value === null || $value === 0) {
return 0;
}

View File

@ -3,6 +3,7 @@
namespace App\Models;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\File;
@ -18,12 +19,13 @@
* @property string $type
* @property string $name
* @property string $disk
* @property bool $is_remote
* @property Server $server
* @property ?Site $site
* @property bool $is_remote
*/
class ServerLog extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServerLogFactory> */
use HasFactory;
protected $fillable = [
@ -45,7 +47,7 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (ServerLog $log) {
self::deleting(function (ServerLog $log): void {
if ($log->is_remote) {
try {
if (Storage::disk($log->disk)->exists($log->name)) {
@ -63,11 +65,17 @@ public function getRouteKey(): string
return 'log';
}
/**
* @return BelongsTo<Server, $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return BelongsTo<Site, $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);
@ -84,7 +92,7 @@ public function download(): StreamedResponse
$this->server->ssh()->download($tmpPath, $this->name);
dispatch(function () use ($tmpPath) {
dispatch(function () use ($tmpPath): void {
if (File::exists($tmpPath)) {
File::delete($tmpPath);
}
@ -98,18 +106,22 @@ public function download(): StreamedResponse
return Storage::disk($this->disk)->download($this->name);
}
public static function getRemote($query, bool $active = true, ?Site $site = null)
/**
* @param Builder<ServerLog> $query
* @return Builder<ServerLog>
*/
public static function getRemote(Builder $query, bool $active = true, ?Site $site = null): Builder
{
$query->where('is_remote', $active);
if ($site) {
if ($site instanceof \App\Models\Site) {
$query->where('name', 'like', $site->path.'%');
}
return $query;
}
public function write($buf): void
public function write(string $buf): void
{
if (Str::contains($buf, 'VITO_SSH_ERROR')) {
$buf = str_replace('VITO_SSH_ERROR', '', $buf);
@ -121,14 +133,14 @@ public function write($buf): void
}
}
public function getContent($lines = null): ?string
public function getContent(?int $lines = null): ?string
{
if ($this->is_remote) {
return $this->server->os()->tail($this->name, $lines ?? 150);
}
if (Storage::disk($this->disk)->exists($this->name)) {
if ($lines) {
if ($lines !== null && $lines !== 0) {
return tail(Storage::disk($this->disk)->path($this->name), $lines);
}
@ -140,9 +152,9 @@ public function getContent($lines = null): ?string
return "Log file doesn't exist!";
}
public static function log(Server $server, string $type, string $content, ?Site $site = null): static
public static function log(Server $server, string $type, string $content, ?Site $site = null): ServerLog
{
$log = new static([
$log = new self([
'server_id' => $server->id,
'site_id' => $site?->id,
'name' => $server->id.'-'.strtotime('now').'-'.$type.'.log',
@ -155,9 +167,9 @@ public static function log(Server $server, string $type, string $content, ?Site
return $log;
}
public static function make(Server $server, string $type): ServerLog
public static function newLog(Server $server, string $type): ServerLog
{
return new static([
return new self([
'server_id' => $server->id,
'name' => $server->id.'-'.strtotime('now').'-'.$type.'.log',
'type' => $type,
@ -169,11 +181,11 @@ public function forSite(Site|int $site): ServerLog
{
if ($site instanceof Site) {
$site = $site->id;
return $this;
}
if (is_int($site)) {
$this->site_id = $site;
}
$this->site_id = $site;
return $this;
}

View File

@ -12,15 +12,16 @@
* @property int $user_id
* @property string $profile
* @property string $provider
* @property array $credentials
* @property array<string, string> $credentials
* @property bool $connected
* @property User $user
* @property ?int $project_id
* @property Server[] $servers
* @property Project $project
* @property ?Project $project
*/
class ServerProvider extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServerProviderFactory> */
use HasFactory;
protected $fillable = [
@ -39,16 +40,25 @@ class ServerProvider extends AbstractModel
'project_id' => 'integer',
];
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* @return array<string, string>
*/
public function getCredentials(): array
{
return $this->credentials;
}
/**
* @return HasMany<Server, covariant $this>
*/
public function servers(): HasMany
{
return $this->hasMany(Server::class, 'provider_id');
@ -58,25 +68,40 @@ public function provider(): \App\ServerProviders\ServerProvider
{
$providerClass = config('core.server_providers_class')[$this->provider];
return new $providerClass($this);
/** @var \App\ServerProviders\ServerProvider $provider */
$provider = new $providerClass($this, new Server);
return $provider;
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return Builder<ServerProvider>
*/
public static function getByProjectId(int $projectId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId) {
/** @var Builder<ServerProvider> $query */
$query = static::query();
return $query
->where(function (Builder $query) use ($projectId): void {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
}
/**
* @return array<string>
*/
public static function regions(?int $id): array
{
if (! $id) {
if ($id === null || $id === 0) {
return [];
}
/** @var ?ServerProvider $profile */
@ -95,9 +120,12 @@ public static function regions(?int $id): array
return $regions;
}
/**
* @return array<string>
*/
public static function plans(?int $id, ?string $region): array
{
if (! $id) {
if ($id === null || $id === 0) {
return [];
}
$profile = self::find($id);

View File

@ -5,7 +5,11 @@
use App\Actions\Service\Manage;
use App\Enums\ServiceStatus;
use App\Exceptions\ServiceInstallationFailed;
use App\SSH\Services\Firewall\Firewall;
use App\SSH\Services\PHP\PHP;
use App\SSH\Services\ProcessManager\ProcessManager;
use App\SSH\Services\ServiceInterface;
use App\SSH\Services\Webserver\Webserver;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;
@ -13,7 +17,7 @@
/**
* @property int $server_id
* @property string $type
* @property array $type_data
* @property array<string, mixed> $type_data
* @property string $name
* @property string $version
* @property string $unit
@ -24,6 +28,7 @@
*/
class Service extends AbstractModel
{
/** @use HasFactory<\Database\Factories\ServiceFactory> */
use HasFactory;
protected $fillable = [
@ -48,13 +53,16 @@ public static function boot(): void
{
parent::boot();
static::creating(function (Service $service) {
static::creating(function (Service $service): void {
if (array_key_exists($service->name, config('core.service_units'))) {
$service->unit = config('core.service_units')[$service->name][$service->server->os][$service->version];
}
});
}
/**
* @var array<string, string>
*/
public static array $statusColors = [
ServiceStatus::READY => 'success',
ServiceStatus::INSTALLING => 'warning',
@ -70,22 +78,28 @@ public static function boot(): void
ServiceStatus::DISABLED => 'gray',
];
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
public function handler(): ServiceInterface
public function handler(): ServiceInterface|Webserver|PHP|Firewall|\App\SSH\Services\Database\Database|ProcessManager
{
$handler = config('core.service_handlers')[$this->name];
return new $handler($this);
/** @var ServiceInterface $service */
$service = new $handler($this);
return $service;
}
/**
* @throws ServiceInstallationFailed
*/
public function validateInstall($result): void
public function validateInstall(string $result): void
{
if (! Str::contains($result, 'Active: active')) {
throw new ServiceInstallationFailed;

View File

@ -22,9 +22,9 @@
/**
* @property int $server_id
* @property string $type
* @property array $type_data
* @property array<string, string> $type_data
* @property string $domain
* @property array $aliases
* @property array<int, string> $aliases
* @property string $web_directory
* @property string $path
* @property string $php_version
@ -39,23 +39,24 @@
* @property string $user
* @property bool $force_ssl
* @property Server $server
* @property ServerLog[] $logs
* @property Deployment[] $deployments
* @property Command[] $commands
* @property Collection<int, ServerLog> $logs
* @property Collection<int, Deployment> $deployments
* @property Collection<int, Command> $commands
* @property ?GitHook $gitHook
* @property DeploymentScript $deploymentScript
* @property Queue[] $queues
* @property Ssl[] $ssls
* @property ?DeploymentScript $deploymentScript
* @property Collection<int, Queue> $queues
* @property Collection<int, Ssl> $ssls
* @property ?Ssl $activeSsl
* @property string $ssh_key_name
* @property ?SourceControl $sourceControl
* @property Collection<LoadBalancerServer> $loadBalancerServers
*
* @TODO: Add nodejs_version column
* @property Collection<int, LoadBalancerServer> $loadBalancerServers
* @property Project $project
*/
class Site extends AbstractModel
{
/** @use HasFactory<\Database\Factories\SiteFactory> */
use HasFactory;
use HasProjectThroughServer;
protected $fillable = [
@ -89,6 +90,9 @@ class Site extends AbstractModel
'force_ssl' => 'boolean',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
SiteStatus::READY => 'success',
SiteStatus::INSTALLING => 'warning',
@ -100,14 +104,17 @@ public static function boot(): void
{
parent::boot();
static::deleting(function (Site $site) {
$site->queues()->each(fn (Queue $queue) => $queue->delete());
static::deleting(function (Site $site): void {
$site->queues()->each(function ($queue): void {
/** @var Queue $queue */
$queue->delete();
});
$site->ssls()->delete();
$site->deployments()->delete();
$site->deploymentScript()->delete();
});
static::created(function (Site $site) {
static::created(function (Site $site): void {
$site->deploymentScript()->create([
'name' => 'default',
'content' => '',
@ -130,51 +137,81 @@ public function isInstallationFailed(): bool
return $this->status === SiteStatus::INSTALLATION_FAILED;
}
/**
* @return BelongsTo<Server, covariant $this>
*/
public function server(): BelongsTo
{
return $this->belongsTo(Server::class);
}
/**
* @return HasMany<ServerLog, covariant $this>
*/
public function logs(): HasMany
{
return $this->hasMany(ServerLog::class);
}
/**
* @return HasMany<Deployment, covariant $this>
*/
public function deployments(): HasMany
{
return $this->hasMany(Deployment::class);
}
/**
* @return HasMany<Command, covariant $this>
*/
public function commands(): HasMany
{
return $this->hasMany(Command::class);
}
/**
* @return HasOne<GitHook, covariant $this>
*/
public function gitHook(): HasOne
{
return $this->hasOne(GitHook::class);
}
/**
* @return HasOne<DeploymentScript, covariant $this>
*/
public function deploymentScript(): HasOne
{
return $this->hasOne(DeploymentScript::class);
}
/**
* @return HasMany<Queue, covariant $this>
*/
public function queues(): HasMany
{
return $this->hasMany(Queue::class);
}
/**
* @return HasMany<Ssl, covariant $this>
*/
public function ssls(): HasMany
{
return $this->hasMany(Ssl::class);
}
/**
* @return MorphToMany<Tag, covariant $this>
*/
public function tags(): MorphToMany
{
return $this->morphToMany(Tag::class, 'taggable');
}
/**
* @return BelongsTo<SourceControl, covariant $this>
*/
public function sourceControl(): BelongsTo
{
return $this->belongsTo(SourceControl::class)->withTrashed();
@ -198,7 +235,10 @@ public function type(): SiteType
{
$typeClass = config('core.site_types_class.'.$this->type);
return new $typeClass($this);
/** @var SiteType $type */
$type = new $typeClass($this);
return $type;
}
public function php(): ?Service
@ -213,23 +253,27 @@ public function php(): ?Service
/**
* @throws SSHError
*/
public function changePHPVersion($version): void
public function changePHPVersion(string $version): void
{
/** @var Webserver $handler */
$handler = $this->server->webserver()->handler();
$handler->changePHPVersion($this, $version);
$webserver = $this->webserver();
$webserver->changePHPVersion($this, $version);
if ($this->isIsolated()) {
/** @var PHP $php */
$php = $this->server->php()->handler();
$php->removeFpmPool($this->user, $this->php_version, $this->id);
$php->createFpmPool($this->user, $version, $this->id);
/** @var Service $php */
$php = $this->server->php();
/** @var PHP $phpHandler */
$phpHandler = $php->handler();
$phpHandler->removeFpmPool($this->user, $this->php_version, $this->id);
$phpHandler->createFpmPool($this->user, $version);
}
$this->php_version = $version;
$this->save();
}
/**
* @return HasOne<Ssl, covariant $this>
*/
public function activeSsl(): HasOne
{
return $this->hasOne(Ssl::class)
@ -318,6 +362,9 @@ public function getEnv(): string
}
}
/**
* @return array<string, string>
*/
public function environmentVariables(?Deployment $deployment = null): array
{
return [
@ -325,7 +372,7 @@ public function environmentVariables(?Deployment $deployment = null): array
'DOMAIN' => $this->domain,
'BRANCH' => $this->branch ?? '',
'REPOSITORY' => $this->repository ?? '',
'COMMIT_ID' => $deployment?->commit_id ?? '',
'COMMIT_ID' => $deployment->commit_id ?? '',
'PHP_VERSION' => $this->php_version,
'PHP_PATH' => '/usr/bin/php'.$this->php_version,
];
@ -338,12 +385,18 @@ public function isIsolated(): bool
public function webserver(): Webserver
{
/** @var Webserver $webserver */
$webserver = $this->server->webserver()->handler();
/** @var Service $webserver */
$webserver = $this->server->webserver();
return $webserver;
/** @var Webserver $handler */
$handler = $webserver->handler();
return $handler;
}
/**
* @return HasMany<LoadBalancerServer, covariant $this>
*/
public function loadBalancerServers(): HasMany
{
return $this->hasMany(LoadBalancerServer::class, 'load_balancer_id');

View File

@ -11,15 +11,17 @@
/**
* @property string $provider
* @property array $provider_data
* @property ?string $profile
* @property array<string, string> $provider_data
* @property string $profile
* @property ?string $url
* @property string $access_token
* @property ?int $project_id
*/
class SourceControl extends AbstractModel
{
/** @use HasFactory<\Database\Factories\SourceControlFactory> */
use HasFactory;
use SoftDeletes;
protected $fillable = [
@ -41,28 +43,43 @@ public function provider(): SourceControlProvider
{
$providerClass = config('core.source_control_providers_class')[$this->provider];
return new $providerClass($this);
/** @var SourceControlProvider $provider */
$provider = new $providerClass($this);
return $provider;
}
public function getRepo(?string $repo = null): ?array
public function getRepo(string $repo): mixed
{
return $this->provider()->getRepo($repo);
}
/**
* @return HasMany<Site, covariant $this>
*/
public function sites(): HasMany
{
return $this->hasMany(Site::class);
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return Builder<SourceControl>
*/
public static function getByProjectId(int $projectId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId) {
/** @var Builder<SourceControl> $query */
$query = static::query();
return $query
->where(function (Builder $query) use ($projectId): void {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
}

View File

@ -16,7 +16,9 @@
*/
class SshKey extends AbstractModel
{
/** @use HasFactory<\Database\Factories\SshKeyFactory> */
use HasFactory;
use SoftDeletes;
protected $fillable = [
@ -30,11 +32,17 @@ class SshKey extends AbstractModel
'public_key' => 'encrypted',
];
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* @return BelongsToMany<Server, covariant $this>
*/
public function servers(): BelongsToMany
{
return $this->belongsToMany(Server::class, 'server_ssh_keys')

View File

@ -17,7 +17,7 @@
* @property Carbon $expires_at
* @property string $status
* @property Site $site
* @property ?array $domains
* @property array<int, string>|string|null $domains
* @property int $log_id
* @property string $email
* @property bool $is_active
@ -28,6 +28,7 @@
*/
class Ssl extends AbstractModel
{
/** @use HasFactory<\Database\Factories\SslFactory> */
use HasFactory;
protected $fillable = [
@ -58,6 +59,9 @@ class Ssl extends AbstractModel
'is_active' => 'boolean',
];
/**
* @var array<string, string>
*/
public static array $statusColors = [
SslStatus::CREATED => 'success',
SslStatus::CREATING => 'warning',
@ -65,6 +69,9 @@ class Ssl extends AbstractModel
SslStatus::FAILED => 'danger',
];
/**
* @return BelongsTo<Site, covariant $this>
*/
public function site(): BelongsTo
{
return $this->belongsTo(Site::class);
@ -85,6 +92,9 @@ public function validateSetup(string $result): bool
return true;
}
/**
* @return array<string>
*/
public function getDomains(): array
{
if (! empty($this->domains) && is_array($this->domains)) {
@ -97,6 +107,9 @@ public function getDomains(): array
return $this->domains;
}
/**
* @return BelongsTo<ServerLog, covariant $this>
*/
public function log(): BelongsTo
{
return $this->belongsTo(ServerLog::class);

View File

@ -11,12 +11,13 @@
* @property int $user_id
* @property string $profile
* @property string $provider
* @property array $credentials
* @property array<string, string> $credentials
* @property User $user
* @property int $project_id
* @property ?int $project_id
*/
class StorageProvider extends AbstractModel
{
/** @use HasFactory<\Database\Factories\StorageProviderFactory> */
use HasFactory;
protected $fillable = [
@ -33,6 +34,9 @@ class StorageProvider extends AbstractModel
'project_id' => 'integer',
];
/**
* @return BelongsTo<User, covariant $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
@ -42,23 +46,38 @@ public function provider(): \App\StorageProviders\StorageProvider
{
$providerClass = config('core.storage_providers_class')[$this->provider];
return new $providerClass($this);
/** @var \App\StorageProviders\StorageProvider $provider */
$provider = new $providerClass($this);
return $provider;
}
/**
* @return HasMany<Backup, covariant $this>
*/
public function backups(): HasMany
{
return $this->hasMany(Backup::class, 'storage_id');
}
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return Builder<StorageProvider>
*/
public static function getByProjectId(int $projectId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId) {
/** @var Builder<StorageProvider> $query */
$query = static::query();
return $query
->where(function (Builder $query) use ($projectId): void {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
}

View File

@ -18,6 +18,7 @@
*/
class Tag extends AbstractModel
{
/** @use HasFactory<\Database\Factories\TagFactory> */
use HasFactory;
protected $fillable = [
@ -30,25 +31,40 @@ class Tag extends AbstractModel
'project_id' => 'int',
];
/**
* @return BelongsTo<Project, covariant $this>
*/
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
/**
* @return MorphToMany<Server, covariant $this>
*/
public function servers(): MorphToMany
{
return $this->morphedByMany(Server::class, 'taggable');
}
/**
* @return MorphToMany<Site, covariant $this>
*/
public function sites(): MorphToMany
{
return $this->morphedByMany(Site::class, 'taggable');
}
/**
* @return Builder<Tag>
*/
public static function getByProjectId(int $projectId): Builder
{
return self::query()
->where(function (Builder $query) use ($projectId) {
/** @var Builder<Tag> $query */
$query = static::query();
return $query
->where(function (Builder $query) use ($projectId): void {
$query->where('project_id', $projectId)->orWhereNull('project_id');
});
}

View File

@ -26,18 +26,19 @@
* @property string $profile_photo_path
* @property string $two_factor_recovery_codes
* @property string $two_factor_secret
* @property SshKey[] $sshKeys
* @property SourceControl[] $sourceControls
* @property ServerProvider[] $serverProviders
* @property Script[] $scripts
* @property StorageProvider[] $storageProviders
* @property StorageProvider[] $connectedStorageProviders
* @property Collection $tokens
* @property Collection<int, SshKey> $sshKeys
* @property Collection<int, SourceControl> $sourceControls
* @property Collection<int, ServerProvider> $serverProviders
* @property Collection<int, Server> $servers
* @property Collection<int, Script> $scripts
* @property Collection<int, StorageProvider> $storageProviders
* @property Collection<int, StorageProvider> $connectedStorageProviders
* @property Collection<int, PersonalAccessToken> $tokens
* @property string $profile_photo_url
* @property string $timezone
* @property int $current_project_id
* @property Project $currentProject
* @property Collection<Project> $projects
* @property ?int $current_project_id
* @property ?Project $currentProject
* @property Collection<int, Project> $projects
* @property string $role
* @property Carbon $created_at
* @property Carbon $updated_at
@ -45,7 +46,10 @@
class User extends Authenticatable implements FilamentUser
{
use HasApiTokens;
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory;
use HasTimezoneTimestamps;
use Notifiable;
use TwoFactorAuthenticatable;
@ -69,41 +73,65 @@ class User extends Authenticatable implements FilamentUser
protected $appends = [
];
/**
* @return HasMany<Server, covariant $this>
*/
public function servers(): HasMany
{
return $this->hasMany(Server::class);
}
/**
* @return HasMany<SshKey, covariant $this>
*/
public function sshKeys(): HasMany
{
return $this->hasMany(SshKey::class);
}
/**
* @return HasMany<SourceControl, covariant $this>
*/
public function sourceControls(): HasMany
{
return $this->hasMany(SourceControl::class);
}
/**
* @return HasMany<ServerProvider, covariant $this>
*/
public function serverProviders(): HasMany
{
return $this->hasMany(ServerProvider::class);
}
/**
* @return HasOne<SourceControl, covariant $this>
*/
public function sourceControl(string $provider): HasOne
{
return $this->hasOne(SourceControl::class)->where('provider', $provider);
}
/**
* @return HasMany<StorageProvider, covariant $this>
*/
public function storageProviders(): HasMany
{
return $this->hasMany(StorageProvider::class);
}
/**
* @return HasOne<StorageProvider, covariant $this>
*/
public function storageProvider(string $provider): HasOne
{
return $this->hasOne(StorageProvider::class)->where('provider', $provider);
}
/**
* @return Builder<Project>|BelongsToMany<Project, covariant $this>
*/
public function allProjects(): Builder|BelongsToMany
{
if ($this->isAdmin()) {
@ -113,12 +141,18 @@ public function allProjects(): Builder|BelongsToMany
return $this->projects();
}
/**
* @return BelongsToMany<Project, covariant $this>
*/
public function projects(): BelongsToMany
{
return $this->belongsToMany(Project::class, 'user_project')
->withTimestamps();
}
/**
* @return HasOne<Project, covariant $this>
*/
public function currentProject(): HasOne
{
return $this->HasOne(Project::class, 'id', 'current_project_id');
@ -126,6 +160,7 @@ public function currentProject(): HasOne
public function createDefaultProject(): Project
{
/** @var ?Project $project */
$project = $this->projects()->first();
if (! $project) {
@ -147,15 +182,24 @@ public function isAdmin(): bool
return $this->role === UserRole::ADMIN;
}
/**
* @return HasMany<Script, covariant $this>
*/
public function scripts(): HasMany
{
return $this->hasMany(Script::class);
}
/**
* @return Builder<Server>
*/
public function allServers(): Builder
{
return Server::query()->whereHas('project', function (Builder $query) {
$query->whereHas('users', function ($query) {
/** @var Builder<Server> $query */
$query = Server::query();
return $query->whereHas('project', function (Builder $query): void {
$query->whereHas('users', function ($query): void {
$query->where('user_id', $this->id);
});
});