mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-02 14:36:17 +00:00
init
This commit is contained in:
24
app/Models/AbstractModel.php
Executable file
24
app/Models/AbstractModel.php
Executable file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property Carbon $created_at
|
||||
* @property Carbon $updated_at
|
||||
*/
|
||||
abstract class AbstractModel extends Model
|
||||
{
|
||||
public function jsonUpdate(string $field, string $key, mixed $value, bool $save = true): void
|
||||
{
|
||||
$current = $this->{$field};
|
||||
$current[$key] = $value;
|
||||
$this->{$field} = $current;
|
||||
if ($save) {
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
}
|
87
app/Models/Backup.php
Normal file
87
app/Models/Backup.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Jobs\Backup\RunBackup;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property string $type
|
||||
* @property string $name
|
||||
* @property int $server_id
|
||||
* @property int $storage_id
|
||||
* @property int $database_id
|
||||
* @property string $interval
|
||||
* @property int $keep_backups
|
||||
* @property string $status
|
||||
* @property Server $server
|
||||
* @property StorageProvider $storage
|
||||
* @property Database $database
|
||||
* @property BackupFile[] $files
|
||||
*/
|
||||
class Backup extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'type',
|
||||
'name',
|
||||
'server_id',
|
||||
'storage_id',
|
||||
'database_id',
|
||||
'interval',
|
||||
'keep_backups',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'storage_id' => 'integer',
|
||||
'database_id' => 'integer',
|
||||
'keep_backups' => 'integer',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Backup $backup) {
|
||||
$backup->files()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function storage(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(StorageProvider::class, 'storage_id');
|
||||
}
|
||||
|
||||
public function database(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Database::class);
|
||||
}
|
||||
|
||||
public function files(): HasMany
|
||||
{
|
||||
return $this->hasMany(BackupFile::class, 'backup_id');
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$file = new BackupFile([
|
||||
'backup_id' => $this->id,
|
||||
'name' => Str::of($this->name)->slug().'-'.now()->format('YmdHis'),
|
||||
'status' => 'creating',
|
||||
]);
|
||||
$file->save();
|
||||
|
||||
dispatch(new RunBackup($file))->onConnection('ssh');
|
||||
}
|
||||
}
|
88
app/Models/BackupFile.php
Normal file
88
app/Models/BackupFile.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Jobs\Backup\RestoreDatabase;
|
||||
use App\Jobs\StorageProvider\DeleteFile;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $backup_id
|
||||
* @property string $name
|
||||
* @property int $size
|
||||
* @property string $status
|
||||
* @property string $restored_to
|
||||
* @property Carbon $restored_at
|
||||
* @property Backup $backup
|
||||
* @property string $path
|
||||
* @property string $storage_path
|
||||
*/
|
||||
class BackupFile extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'backup_id',
|
||||
'name',
|
||||
'size',
|
||||
'status',
|
||||
'restored_to',
|
||||
'restored_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'backup_id' => 'integer',
|
||||
'restored_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::created(function (BackupFile $backupFile) {
|
||||
$keep = $backupFile->backup->keep_backups;
|
||||
if ($backupFile->backup->files()->count() > $keep) {
|
||||
/* @var BackupFile $lastFileToKeep */
|
||||
$lastFileToKeep = $backupFile->backup->files()->orderByDesc('id')->skip($keep)->first();
|
||||
if ($lastFileToKeep) {
|
||||
$files = $backupFile->backup->files()
|
||||
->where('id', '<=', $lastFileToKeep->id)
|
||||
->get();
|
||||
foreach ($files as $file) {
|
||||
$file->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
static::deleted(function (BackupFile $backupFile) {
|
||||
dispatch(new DeleteFile(
|
||||
$backupFile->backup->storage,
|
||||
[$backupFile->storage_path]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
public function backup(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Backup::class);
|
||||
}
|
||||
|
||||
public function getPathAttribute(): string
|
||||
{
|
||||
return '/home/'.$this->backup->server->ssh_user.'/'.$this->name.'.zip';
|
||||
}
|
||||
|
||||
public function getStoragePathAttribute(): string
|
||||
{
|
||||
return '/'.$this->backup->name.'/'.$this->name.'.zip';
|
||||
}
|
||||
|
||||
public function restore(Database $database): void
|
||||
{
|
||||
$this->status = 'restoring';
|
||||
$this->restored_to = $database->name;
|
||||
$this->save();
|
||||
dispatch(new RestoreDatabase($this, $database))->onConnection('ssh');
|
||||
}
|
||||
}
|
90
app/Models/CronJob.php
Executable file
90
app/Models/CronJob.php
Executable file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\CronjobStatus;
|
||||
use App\Jobs\CronJob\AddToServer;
|
||||
use App\Jobs\CronJob\RemoveFromServer;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $command
|
||||
* @property string $user
|
||||
* @property string $frequency
|
||||
* @property string $frequency_label
|
||||
* @property bool $hidden
|
||||
* @property string $status
|
||||
* @property string $crontab
|
||||
* @property Server $server
|
||||
*/
|
||||
class CronJob extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'command',
|
||||
'user',
|
||||
'frequency',
|
||||
'hidden',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'hidden' => 'boolean',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'frequency_label',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function getCrontabAttribute(): string
|
||||
{
|
||||
$data = '';
|
||||
$cronJobs = $this->server->cronJobs()->where('user', $this->user)->get();
|
||||
foreach ($cronJobs as $key => $cronJob) {
|
||||
$data .= $cronJob->frequency.' '.$cronJob->command;
|
||||
if ($key != count($cronJobs) - 1) {
|
||||
$data .= "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function addToServer(): void
|
||||
{
|
||||
dispatch(new AddToServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function removeFromServer(): void
|
||||
{
|
||||
$this->status = CronjobStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new RemoveFromServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getFrequencyLabelAttribute(): string
|
||||
{
|
||||
$labels = [
|
||||
'* * * * *' => 'Every minute',
|
||||
'0 * * * *' => 'Hourly',
|
||||
'0 0 * * *' => 'Daily',
|
||||
'0 0 * * 0' => 'Weekly',
|
||||
'0 0 1 * *' => 'Monthly',
|
||||
];
|
||||
if (isset($labels[$this->frequency])) {
|
||||
return $labels[$this->frequency];
|
||||
}
|
||||
|
||||
return $this->frequency;
|
||||
}
|
||||
}
|
79
app/Models/Database.php
Executable file
79
app/Models/Database.php
Executable file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\DatabaseStatus;
|
||||
use App\Jobs\Database\CreateOnServer;
|
||||
use App\Jobs\Database\DeleteFromServer;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $name
|
||||
* @property string $status
|
||||
* @property Server $server
|
||||
* @property Backup[] $backups
|
||||
*/
|
||||
class Database extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'name',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Database $database) {
|
||||
$database->server->databaseUsers()->each(function (DatabaseUser $user) use ($database) {
|
||||
$databases = $user->databases;
|
||||
if ($databases && in_array($database->name, $databases)) {
|
||||
unset($databases[array_search($database->name, $databases)]);
|
||||
$user->databases = $databases;
|
||||
$user->save();
|
||||
}
|
||||
});
|
||||
$database->backups()->each(function (Backup $backup) {
|
||||
$backup->delete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* create database on server
|
||||
*/
|
||||
public function createOnServer(): void
|
||||
{
|
||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
/**
|
||||
* delete database from server
|
||||
*/
|
||||
public function deleteFromServer(): void
|
||||
{
|
||||
$this->status = DatabaseStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function backups(): HasMany
|
||||
{
|
||||
return $this->hasMany(Backup::class)->where('type', 'database');
|
||||
}
|
||||
}
|
96
app/Models/DatabaseUser.php
Executable file
96
app/Models/DatabaseUser.php
Executable file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\DatabaseStatus;
|
||||
use App\Jobs\DatabaseUser\CreateOnServer;
|
||||
use App\Jobs\DatabaseUser\DeleteFromServer;
|
||||
use App\Jobs\DatabaseUser\LinkUser;
|
||||
use App\Jobs\DatabaseUser\UnlinkUser;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $username
|
||||
* @property string $password
|
||||
* @property array $databases
|
||||
* @property string $host
|
||||
* @property string $status
|
||||
* @property Server $server
|
||||
* @property string $full_user
|
||||
*/
|
||||
class DatabaseUser extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'username',
|
||||
'password',
|
||||
'databases',
|
||||
'host',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'password' => 'encrypted',
|
||||
'databases' => 'array',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function scopeHasDatabase(Builder $query, string $databaseName): Builder
|
||||
{
|
||||
return $query->where('databases', 'like', "%\"$databaseName\"%");
|
||||
}
|
||||
|
||||
public function createOnServer(): void
|
||||
{
|
||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function deleteFromServer(): void
|
||||
{
|
||||
$this->status = DatabaseStatus::DELETING;
|
||||
$this->save();
|
||||
|
||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function linkNewDatabase(string $name): void
|
||||
{
|
||||
$linkedDatabases = $this->databases ?? [];
|
||||
if (! in_array($name, $linkedDatabases)) {
|
||||
$linkedDatabases[] = $name;
|
||||
$this->databases = $linkedDatabases;
|
||||
$this->unlinkUser();
|
||||
$this->linkUser();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function linkUser(): void
|
||||
{
|
||||
dispatch(new LinkUser($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function unlinkUser(): void
|
||||
{
|
||||
dispatch(new UnlinkUser($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getFullUserAttribute(): string
|
||||
{
|
||||
return $this->username.'@'.$this->host;
|
||||
}
|
||||
}
|
67
app/Models/Deployment.php
Executable file
67
app/Models/Deployment.php
Executable file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property int $deployment_script_id
|
||||
* @property int $log_id
|
||||
* @property string $commit_id
|
||||
* @property string $commit_id_short
|
||||
* @property array $commit_data
|
||||
* @property string $status
|
||||
* @property Site $site
|
||||
* @property DeploymentScript $deploymentScript
|
||||
* @property ServerLog $log
|
||||
*/
|
||||
class Deployment extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'deployment_script_id',
|
||||
'log_id',
|
||||
'commit_id',
|
||||
'commit_data',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
'deployment_script_id' => 'integer',
|
||||
'log_id' => 'integer',
|
||||
'commit_data' => 'json',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'commit_id_short',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function deploymentScript(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(DeploymentScript::class);
|
||||
}
|
||||
|
||||
public function log(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ServerLog::class, 'log_id');
|
||||
}
|
||||
|
||||
public function getCommitIdShortAttribute(): string
|
||||
{
|
||||
if ($this->commit_id) {
|
||||
return substr($this->commit_id, 0, 7);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
32
app/Models/DeploymentScript.php
Normal file
32
app/Models/DeploymentScript.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property string $name
|
||||
* @property string $content
|
||||
* @property Site $site
|
||||
*/
|
||||
class DeploymentScript extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'name',
|
||||
'content',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
}
|
68
app/Models/FirewallRule.php
Executable file
68
app/Models/FirewallRule.php
Executable file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\FirewallRuleStatus;
|
||||
use App\Jobs\Firewall\AddToServer;
|
||||
use App\Jobs\Firewall\RemoveFromServer;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $type
|
||||
* @property string $protocol
|
||||
* @property string $real_protocol
|
||||
* @property int $port
|
||||
* @property string $source
|
||||
* @property string $mask
|
||||
* @property string $note
|
||||
* @property string $status
|
||||
* @property Server $server
|
||||
*/
|
||||
class FirewallRule extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'type',
|
||||
'protocol',
|
||||
'port',
|
||||
'source',
|
||||
'mask',
|
||||
'note',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'port' => 'integer',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'real_protocol',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function addToServer(): void
|
||||
{
|
||||
dispatch(new AddToServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function removeFromServer(): void
|
||||
{
|
||||
$this->status = FirewallRuleStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new RemoveFromServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getRealProtocolAttribute(): string
|
||||
{
|
||||
return $this->protocol === 'udp' ? 'udp' : 'tcp';
|
||||
}
|
||||
}
|
83
app/Models/GitHook.php
Executable file
83
app/Models/GitHook.php
Executable file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\FailedToDeployGitHook;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property int $source_control_id
|
||||
* @property string $secret
|
||||
* @property array $events
|
||||
* @property array $actions
|
||||
* @property string $hook_id
|
||||
* @property array $hook_response
|
||||
* @property Site $site
|
||||
* @property SourceControl $sourceControl
|
||||
*/
|
||||
class GitHook extends AbstractModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'source_control_id',
|
||||
'secret',
|
||||
'events',
|
||||
'actions',
|
||||
'hook_id',
|
||||
'hook_response',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
'source_control_id' => 'integer',
|
||||
'events' => 'array',
|
||||
'actions' => 'array',
|
||||
'hook_response' => 'json',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function sourceControl(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(SourceControl::class);
|
||||
}
|
||||
|
||||
public function scopeHasEvent(Builder $query, string $event): Builder
|
||||
{
|
||||
return $query->where('events', 'like', "%\"{$event}\"%");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FailedToDeployGitHook
|
||||
*/
|
||||
public function deployHook(): void
|
||||
{
|
||||
$this->update(
|
||||
$this->sourceControl->provider()->deployHook($this->site->repository, $this->events, $this->secret)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function destroyHook(): void
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->sourceControl->provider()->destroyHook($this->site->repository, $this->hook_id);
|
||||
$this->delete();
|
||||
DB::commit();
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
39
app/Models/NotificationChannel.php
Normal file
39
app/Models/NotificationChannel.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property string $provider
|
||||
* @property string $label
|
||||
* @property array $data
|
||||
* @property bool $connected
|
||||
* @property bool $is_default
|
||||
* @property User $user
|
||||
*/
|
||||
class NotificationChannel extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'label',
|
||||
'data',
|
||||
'connected',
|
||||
'is_default',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'data' => 'json',
|
||||
'connected' => 'boolean',
|
||||
'is_default' => 'boolean',
|
||||
];
|
||||
|
||||
public function provider(): \App\Contracts\NotificationChannel
|
||||
{
|
||||
$provider = config('core.notification_channels_providers_class')[$this->provider];
|
||||
|
||||
return new $provider($this);
|
||||
}
|
||||
}
|
143
app/Models/Queue.php
Normal file
143
app/Models/Queue.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\QueueStatus;
|
||||
use App\Jobs\Queue\Deploy;
|
||||
use App\Jobs\Queue\GetLogs;
|
||||
use App\Jobs\Queue\Manage;
|
||||
use App\Jobs\Queue\Remove;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property int $site_id
|
||||
* @property string $command
|
||||
* @property string $user
|
||||
* @property bool $auto_start
|
||||
* @property bool $auto_restart
|
||||
* @property int $numprocs
|
||||
* @property int $redirect_stderr
|
||||
* @property string $stdout_logfile
|
||||
* @property string $status
|
||||
* @property string $log_directory
|
||||
* @property string $log_file
|
||||
* @property Server $server
|
||||
* @property Site $site
|
||||
*/
|
||||
class Queue extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'site_id',
|
||||
'command',
|
||||
'user',
|
||||
'auto_start',
|
||||
'auto_restart',
|
||||
'numprocs',
|
||||
'redirect_stderr',
|
||||
'stdout_logfile',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'site_id' => 'integer',
|
||||
'auto_start' => 'boolean',
|
||||
'auto_restart' => 'boolean',
|
||||
'numprocs' => 'integer',
|
||||
'redirect_stderr' => 'boolean',
|
||||
];
|
||||
|
||||
public function getServerIdAttribute(int $value): int
|
||||
{
|
||||
if (! $value) {
|
||||
$value = $this->site->server_id;
|
||||
$this->fill(['server_id' => $this->site->server_id]);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function getLogDirectoryAttribute(): string
|
||||
{
|
||||
return '/home/'.$this->user.'/.logs/workers';
|
||||
}
|
||||
|
||||
public function getLogFileAttribute(): string
|
||||
{
|
||||
return $this->log_directory.'/'.$this->id.'.log';
|
||||
}
|
||||
|
||||
public function deploy(): void
|
||||
{
|
||||
dispatch(new Deploy($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
private function action(string $type, string $status, string $successStatus, string $failStatus, string $failMessage): void
|
||||
{
|
||||
$this->status = $status;
|
||||
$this->save();
|
||||
dispatch(new Manage($this, $type, $successStatus, $failStatus, $failMessage))
|
||||
->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->action(
|
||||
'start',
|
||||
QueueStatus::STARTING,
|
||||
QueueStatus::RUNNING,
|
||||
QueueStatus::FAILED,
|
||||
__('Failed to start')
|
||||
);
|
||||
}
|
||||
|
||||
public function stop(): void
|
||||
{
|
||||
$this->action(
|
||||
'stop',
|
||||
QueueStatus::STOPPING,
|
||||
QueueStatus::STOPPED,
|
||||
QueueStatus::FAILED,
|
||||
__('Failed to stop')
|
||||
);
|
||||
}
|
||||
|
||||
public function restart(): void
|
||||
{
|
||||
$this->action(
|
||||
'restart',
|
||||
QueueStatus::RESTARTING,
|
||||
QueueStatus::RUNNING,
|
||||
QueueStatus::FAILED,
|
||||
__('Failed to restart')
|
||||
);
|
||||
}
|
||||
|
||||
public function remove(): void
|
||||
{
|
||||
$this->status = QueueStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new Remove($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getLogs(): void
|
||||
{
|
||||
dispatch(new GetLogs($this))->onConnection('ssh');
|
||||
}
|
||||
}
|
50
app/Models/Redirect.php
Normal file
50
app/Models/Redirect.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Jobs\Redirect\AddToServer;
|
||||
use App\Jobs\Redirect\DeleteFromServer;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property int $mode
|
||||
* @property string $from
|
||||
* @property string $to
|
||||
* @property string $status
|
||||
*/
|
||||
class Redirect extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'mode',
|
||||
'from',
|
||||
'to',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
'mode' => 'integer',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function addToServer(): void
|
||||
{
|
||||
dispatch(new AddToServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function deleteFromServer(): void
|
||||
{
|
||||
$this->status = 'deleting';
|
||||
$this->save();
|
||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
||||
}
|
||||
}
|
44
app/Models/Script.php
Normal file
44
app/Models/Script.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Jobs\Script\ExecuteOn;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $name
|
||||
* @property string $content
|
||||
* @property User $creator
|
||||
*/
|
||||
class Script extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'content',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function executions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ScriptExecution::class, 'script_id');
|
||||
}
|
||||
|
||||
public function executeOn(Server $server, string $user): void
|
||||
{
|
||||
dispatch(new ExecuteOn($this, $server, $user))->onConnection('ssh');
|
||||
}
|
||||
}
|
42
app/Models/ScriptExecution.php
Normal file
42
app/Models/ScriptExecution.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $script_id
|
||||
* @property int $server_id
|
||||
* @property string $user
|
||||
* @property Carbon $finished_at
|
||||
* @property ?Server $server
|
||||
* @property Script $script
|
||||
*/
|
||||
class ScriptExecution extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'script_id',
|
||||
'server_id',
|
||||
'user',
|
||||
'finished_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'script_id' => 'integer',
|
||||
'server_id' => 'integer',
|
||||
];
|
||||
|
||||
public function script(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Script::class);
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
}
|
388
app/Models/Server.php
Executable file
388
app/Models/Server.php
Executable file
@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\ServerType;
|
||||
use App\Facades\SSH;
|
||||
use App\Jobs\Installation\Upgrade;
|
||||
use App\Jobs\Server\CheckConnection;
|
||||
use App\Jobs\Server\RebootServer;
|
||||
use App\Support\Testing\SSHFake;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $name
|
||||
* @property string $ssh_user
|
||||
* @property string $ip
|
||||
* @property string $local_ip
|
||||
* @property int $port
|
||||
* @property string $os
|
||||
* @property string $type
|
||||
* @property array $type_data
|
||||
* @property string $provider
|
||||
* @property int $provider_id
|
||||
* @property array $provider_data
|
||||
* @property array $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 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 ScriptExecution[] $scriptExecutions
|
||||
* @property Backup[] $backups
|
||||
* @property Queue[] $daemons
|
||||
* @property SshKey[] $sshKeys
|
||||
* @property string $hostname
|
||||
*/
|
||||
class Server extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'ssh_user',
|
||||
'ip',
|
||||
'local_ip',
|
||||
'port',
|
||||
'os',
|
||||
'type',
|
||||
'type_data',
|
||||
'provider',
|
||||
'provider_id',
|
||||
'provider_data',
|
||||
'authentication',
|
||||
'public_key',
|
||||
'status',
|
||||
'auto_update',
|
||||
'available_updates',
|
||||
'security_updates',
|
||||
'progress',
|
||||
'progress_step',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'type_data' => 'json',
|
||||
'port' => 'integer',
|
||||
'provider_data' => 'json',
|
||||
'authentication' => 'encrypted:json',
|
||||
'auto_update' => 'boolean',
|
||||
'available_updates' => 'integer',
|
||||
'security_updates' => 'integer',
|
||||
'progress' => 'integer',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'authentication',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Server $server) {
|
||||
$server->sites()->each(function (Site $site) {
|
||||
$site->delete();
|
||||
});
|
||||
$server->provider()->delete();
|
||||
$server->logs()->delete();
|
||||
$server->services()->delete();
|
||||
$server->databases()->delete();
|
||||
$server->databaseUsers()->delete();
|
||||
$server->firewallRules()->delete();
|
||||
$server->cronJobs()->delete();
|
||||
$server->queues()->delete();
|
||||
$server->daemons()->delete();
|
||||
$server->scriptExecutions()->delete();
|
||||
$server->sshKeys()->detach();
|
||||
if (File::exists($server->sshKey()['public_key_path'])) {
|
||||
File::delete($server->sshKey()['public_key_path']);
|
||||
}
|
||||
if (File::exists($server->sshKey()['private_key_path'])) {
|
||||
File::delete($server->sshKey()['private_key_path']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function serverProvider(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ServerProvider::class, 'provider_id');
|
||||
}
|
||||
|
||||
public function logs(): HasMany
|
||||
{
|
||||
return $this->hasMany(ServerLog::class);
|
||||
}
|
||||
|
||||
public function sites(): HasMany
|
||||
{
|
||||
return $this->hasMany(Site::class);
|
||||
}
|
||||
|
||||
public function services(): HasMany
|
||||
{
|
||||
return $this->hasMany(Service::class);
|
||||
}
|
||||
|
||||
public function databases(): HasMany
|
||||
{
|
||||
return $this->hasMany(Database::class);
|
||||
}
|
||||
|
||||
public function databaseUsers(): HasMany
|
||||
{
|
||||
return $this->hasMany(DatabaseUser::class);
|
||||
}
|
||||
|
||||
public function firewallRules(): HasMany
|
||||
{
|
||||
return $this->hasMany(FirewallRule::class);
|
||||
}
|
||||
|
||||
public function cronJobs(): HasMany
|
||||
{
|
||||
return $this->hasMany(CronJob::class);
|
||||
}
|
||||
|
||||
public function queues(): HasMany
|
||||
{
|
||||
return $this->hasMany(Queue::class);
|
||||
}
|
||||
|
||||
public function scriptExecutions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ScriptExecution::class);
|
||||
}
|
||||
|
||||
public function backups(): HasMany
|
||||
{
|
||||
return $this->hasMany(Backup::class);
|
||||
}
|
||||
|
||||
public function daemons(): HasMany
|
||||
{
|
||||
return $this->queues()->whereNull('site_id');
|
||||
}
|
||||
|
||||
public function service($type, $version = null): ?Service
|
||||
{
|
||||
/* @var Service $service */
|
||||
$service = $this->services()
|
||||
->where(function ($query) use ($type, $version) {
|
||||
$query->where('type', $type);
|
||||
if ($version) {
|
||||
$query->where('version', $version);
|
||||
}
|
||||
})
|
||||
->first();
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function defaultService($type): ?Service
|
||||
{
|
||||
/* @var Service $service */
|
||||
$service = $this->services()
|
||||
->where('type', $type)
|
||||
->where('is_default', 1)
|
||||
->first();
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function getServiceByUnit($unit): ?Service
|
||||
{
|
||||
/* @var Service $service */
|
||||
$service = $this->services()
|
||||
->where('unit', $unit)
|
||||
->where('is_default', 1)
|
||||
->first();
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
$this->type()->install();
|
||||
// $this->team->notify(new ServerInstallationStarted($this));
|
||||
}
|
||||
|
||||
public function ssh(string $user = null, bool $defaultKeys = false): \App\Helpers\SSH|SSHFake
|
||||
{
|
||||
return SSH::init($this, $user, $defaultKeys);
|
||||
}
|
||||
|
||||
public function installedPHPVersions(): array
|
||||
{
|
||||
$versions = [];
|
||||
$phps = $this->services()->where('type', 'php')->get(['version']);
|
||||
foreach ($phps as $php) {
|
||||
$versions[] = $php->version;
|
||||
}
|
||||
|
||||
return $versions;
|
||||
}
|
||||
|
||||
public function type(): ServerType
|
||||
{
|
||||
$typeClass = config('core.server_types_class')[$this->type];
|
||||
|
||||
return new $typeClass($this);
|
||||
}
|
||||
|
||||
public function provider(): \App\Contracts\ServerProvider
|
||||
{
|
||||
$providerClass = config('core.server_providers_class')[$this->provider];
|
||||
|
||||
return new $providerClass($this);
|
||||
}
|
||||
|
||||
public function webserver(string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('webserver');
|
||||
}
|
||||
|
||||
return $this->service('webserver', $version);
|
||||
}
|
||||
|
||||
public function database(string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('database');
|
||||
}
|
||||
|
||||
return $this->service('database', $version);
|
||||
}
|
||||
|
||||
public function firewall(string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('firewall');
|
||||
}
|
||||
|
||||
return $this->service('firewall', $version);
|
||||
}
|
||||
|
||||
public function processManager(string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('process_manager');
|
||||
}
|
||||
|
||||
return $this->service('process_manager', $version);
|
||||
}
|
||||
|
||||
public function php(string $version = null): ?Service
|
||||
{
|
||||
if (! $version) {
|
||||
return $this->defaultService('php');
|
||||
}
|
||||
|
||||
return $this->service('php', $version);
|
||||
}
|
||||
|
||||
public function sshKeys(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(SshKey::class, 'server_ssh_keys')
|
||||
->withPivot('status')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function getSshUserAttribute(string $value): string
|
||||
{
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return config('core.ssh_user');
|
||||
}
|
||||
|
||||
public function sshKey(bool $default = false): array
|
||||
{
|
||||
if (app()->environment() == 'testing') {
|
||||
return [
|
||||
'public_key' => 'public',
|
||||
'public_key_path' => '/path',
|
||||
'private_key_path' => '/path',
|
||||
];
|
||||
}
|
||||
|
||||
if ($default) {
|
||||
return [
|
||||
'public_key' => Str::replace("\n", '', File::get(storage_path(config('core.ssh_public_key_name')))),
|
||||
'public_key_path' => storage_path(config('core.ssh_public_key_name')),
|
||||
'private_key_path' => storage_path(config('core.ssh_private_key_name')),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'public_key' => Str::replace("\n", '', Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub')),
|
||||
'public_key_path' => Storage::disk(config('core.key_pairs_disk'))->path($this->id.'.pub'),
|
||||
'private_key_path' => Storage::disk(config('core.key_pairs_disk'))->path((string) $this->id),
|
||||
];
|
||||
}
|
||||
|
||||
public function getServiceUnits(): array
|
||||
{
|
||||
$units = [];
|
||||
$services = $this->services;
|
||||
foreach ($services as $service) {
|
||||
if ($service->unit) {
|
||||
$units[] = $service->unit;
|
||||
}
|
||||
}
|
||||
|
||||
return $units;
|
||||
}
|
||||
|
||||
public function checkConnection(): void
|
||||
{
|
||||
dispatch(new CheckConnection($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function installUpdates(): void
|
||||
{
|
||||
$this->available_updates = 0;
|
||||
$this->security_updates = 0;
|
||||
$this->save();
|
||||
dispatch(new Upgrade($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function reboot(): void
|
||||
{
|
||||
$this->status = 'disconnected';
|
||||
$this->save();
|
||||
dispatch(new RebootServer($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getHostnameAttribute(): string
|
||||
{
|
||||
return Str::of($this->name)->slug();
|
||||
}
|
||||
}
|
72
app/Models/ServerLog.php
Executable file
72
app/Models/ServerLog.php
Executable file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property ?int $site_id
|
||||
* @property string $type
|
||||
* @property string $name
|
||||
* @property string $disk
|
||||
* @property Server $server
|
||||
* @property ?Site $site
|
||||
* @property string $content
|
||||
*/
|
||||
class ServerLog extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'site_id',
|
||||
'type',
|
||||
'name',
|
||||
'disk',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'site_id' => 'integer',
|
||||
];
|
||||
|
||||
public function getRouteKey(): string
|
||||
{
|
||||
return 'log';
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function write($buf): void
|
||||
{
|
||||
if (Str::contains($buf, 'VITO_SSH_ERROR')) {
|
||||
$buf = str_replace('VITO_SSH_ERROR', '', $buf);
|
||||
}
|
||||
if (Storage::disk($this->disk)->exists($this->name)) {
|
||||
Storage::disk($this->disk)->append($this->name, $buf);
|
||||
} else {
|
||||
Storage::disk($this->disk)->put($this->name, $buf);
|
||||
}
|
||||
}
|
||||
|
||||
public function getContentAttribute(): ?string
|
||||
{
|
||||
if (Storage::disk($this->disk)->exists($this->name)) {
|
||||
return Storage::disk($this->disk)->get($this->name);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
43
app/Models/ServerProvider.php
Normal file
43
app/Models/ServerProvider.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $profile
|
||||
* @property string $provider
|
||||
* @property array $credentials
|
||||
* @property bool $connected
|
||||
* @property User $user
|
||||
*/
|
||||
class ServerProvider extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'profile',
|
||||
'provider',
|
||||
'credentials',
|
||||
'connected',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'credentials' => 'encrypted:array',
|
||||
'connected' => 'boolean',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function getCredentials(): array
|
||||
{
|
||||
return $this->credentials;
|
||||
}
|
||||
}
|
217
app/Models/Service.php
Executable file
217
app/Models/Service.php
Executable file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\Database;
|
||||
use App\Contracts\Firewall;
|
||||
use App\Contracts\ProcessManager;
|
||||
use App\Contracts\Webserver;
|
||||
use App\Enums\ServiceStatus;
|
||||
use App\Events\Broadcast;
|
||||
use App\Exceptions\InstallationFailed;
|
||||
use App\Jobs\Service\Manage;
|
||||
use App\ServiceHandlers\PHP;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $type
|
||||
* @property array $type_data
|
||||
* @property string $name
|
||||
* @property string $version
|
||||
* @property string $unit
|
||||
* @property string $logs
|
||||
* @property string $status
|
||||
* @property bool $is_default
|
||||
* @property Server $server
|
||||
*/
|
||||
class Service extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'type',
|
||||
'type_data',
|
||||
'name',
|
||||
'version',
|
||||
'unit',
|
||||
'logs',
|
||||
'status',
|
||||
'is_default',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'type_data' => 'json',
|
||||
'is_default' => 'boolean',
|
||||
];
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function handler(): Database|Firewall|Webserver|PHP|ProcessManager
|
||||
{
|
||||
$handler = config('core.service_handlers')[$this->name];
|
||||
|
||||
return new $handler($this);
|
||||
}
|
||||
|
||||
public function installer(): mixed
|
||||
{
|
||||
$installer = config('core.service_installers')[$this->name];
|
||||
|
||||
return new $installer($this);
|
||||
}
|
||||
|
||||
public function uninstaller(): mixed
|
||||
{
|
||||
$uninstaller = config('core.service_uninstallers')[$this->name];
|
||||
|
||||
return new $uninstaller($this);
|
||||
}
|
||||
|
||||
public function getUnitAttribute($value): string
|
||||
{
|
||||
if ($value) {
|
||||
return $value;
|
||||
}
|
||||
if (isset(config('core.service_units')[$this->name])) {
|
||||
$value = config('core.service_units')[$this->name][$this->server->os][$this->version];
|
||||
if ($value) {
|
||||
$this->fill(['unit' => $value]);
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
Bus::chain([
|
||||
$this->installer(),
|
||||
function () {
|
||||
event(
|
||||
new Broadcast('install-service-finished', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
},
|
||||
])->catch(function () {
|
||||
event(
|
||||
new Broadcast('install-service-failed', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
})->onConnection('ssh-long')->dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InstallationFailed
|
||||
*/
|
||||
public function validateInstall($result): void
|
||||
{
|
||||
if (Str::contains($result, 'Active: active')) {
|
||||
event(
|
||||
new Broadcast('install-service-finished', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
} else {
|
||||
event(
|
||||
new Broadcast('install-service-failed', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
throw new InstallationFailed();
|
||||
}
|
||||
}
|
||||
|
||||
public function uninstall(): void
|
||||
{
|
||||
$this->status = ServiceStatus::UNINSTALLING;
|
||||
$this->save();
|
||||
Bus::chain([
|
||||
$this->uninstaller(),
|
||||
function () {
|
||||
event(
|
||||
new Broadcast('uninstall-service-finished', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
$this->delete();
|
||||
},
|
||||
])->catch(function () {
|
||||
$this->status = ServiceStatus::FAILED;
|
||||
$this->save();
|
||||
event(
|
||||
new Broadcast('uninstall-service-failed', [
|
||||
'service' => $this,
|
||||
])
|
||||
);
|
||||
})->onConnection('ssh')->dispatch();
|
||||
}
|
||||
|
||||
public function start(): void
|
||||
{
|
||||
$this->action(
|
||||
'start',
|
||||
ServiceStatus::STARTING,
|
||||
ServiceStatus::READY,
|
||||
ServiceStatus::STOPPED,
|
||||
__('Failed to start')
|
||||
);
|
||||
}
|
||||
|
||||
public function stop(): void
|
||||
{
|
||||
$this->action(
|
||||
'stop',
|
||||
ServiceStatus::STOPPING,
|
||||
ServiceStatus::STOPPED,
|
||||
ServiceStatus::FAILED,
|
||||
__('Failed to stop')
|
||||
);
|
||||
}
|
||||
|
||||
public function restart(): void
|
||||
{
|
||||
$this->action(
|
||||
'restart',
|
||||
ServiceStatus::RESTARTING,
|
||||
ServiceStatus::READY,
|
||||
ServiceStatus::FAILED,
|
||||
__('Failed to restart')
|
||||
);
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $type,
|
||||
string $status,
|
||||
string $successStatus,
|
||||
string $failStatus,
|
||||
string $failMessage
|
||||
): void {
|
||||
$this->status = $status;
|
||||
$this->save();
|
||||
dispatch(new Manage($this, $type, $successStatus, $failStatus, $failMessage))
|
||||
->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function installedVersions(): array
|
||||
{
|
||||
$versions = [];
|
||||
$services = $this->server->services()->where('type', $this->type)->get(['version']);
|
||||
foreach ($services as $service) {
|
||||
$versions[] = $service->version;
|
||||
}
|
||||
|
||||
return $versions;
|
||||
}
|
||||
}
|
397
app/Models/Site.php
Executable file
397
app/Models/Site.php
Executable file
@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\SiteType;
|
||||
use App\Enums\DeploymentStatus;
|
||||
use App\Enums\SiteStatus;
|
||||
use App\Enums\SslStatus;
|
||||
use App\Exceptions\FailedToDeployGitHook;
|
||||
use App\Exceptions\SourceControlIsNotConnected;
|
||||
use App\Jobs\Site\ChangePHPVersion;
|
||||
use App\Jobs\Site\Deploy;
|
||||
use App\Jobs\Site\DeployEnv;
|
||||
use App\Jobs\Site\UpdateBranch;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $type
|
||||
* @property array $type_data
|
||||
* @property string $domain
|
||||
* @property array $aliases
|
||||
* @property string $web_directory
|
||||
* @property string $web_directory_path
|
||||
* @property string $path
|
||||
* @property string $php_version
|
||||
* @property string $source_control
|
||||
* @property string $repository
|
||||
* @property string $branch
|
||||
* @property string $status
|
||||
* @property int $port
|
||||
* @property int $progress
|
||||
* @property bool $auto_deployment
|
||||
* @property string $url
|
||||
* @property Server $server
|
||||
* @property ServerLog[] $logs
|
||||
* @property Deployment[] $deployments
|
||||
* @property ?GitHook $gitHook
|
||||
* @property DeploymentScript $deploymentScript
|
||||
* @property Redirect[] $redirects
|
||||
* @property Queue[] $queues
|
||||
* @property Ssl[] $ssls
|
||||
* @property ?Ssl $activeSsl
|
||||
* @property string $full_repository_url
|
||||
* @property string $aliases_string
|
||||
* @property string $deployment_script_text
|
||||
* @property string $env
|
||||
*/
|
||||
class Site extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'type',
|
||||
'type_data',
|
||||
'domain',
|
||||
'aliases',
|
||||
'web_directory',
|
||||
'path',
|
||||
'php_version',
|
||||
'source_control',
|
||||
'repository',
|
||||
'branch',
|
||||
'status',
|
||||
'port',
|
||||
'progress',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'server_id' => 'integer',
|
||||
'type_data' => 'json',
|
||||
'port' => 'integer',
|
||||
'progress' => 'integer',
|
||||
'auto_deployment' => 'boolean',
|
||||
'aliases' => 'array',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'url',
|
||||
'auto_deployment',
|
||||
];
|
||||
|
||||
public static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function (Site $site) {
|
||||
$site->redirects()->delete();
|
||||
$site->queues()->delete();
|
||||
$site->ssls()->delete();
|
||||
$site->deployments()->delete();
|
||||
$site->deploymentScript()->delete();
|
||||
});
|
||||
|
||||
static::created(function (Site $site) {
|
||||
$site->deploymentScript()->create([
|
||||
'name' => 'default',
|
||||
'content' => ''
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function server(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
|
||||
public function logs(): HasMany
|
||||
{
|
||||
return $this->hasMany(ServerLog::class);
|
||||
}
|
||||
|
||||
public function deployments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Deployment::class);
|
||||
}
|
||||
|
||||
public function gitHook(): HasOne
|
||||
{
|
||||
return $this->hasOne(GitHook::class);
|
||||
}
|
||||
|
||||
public function deploymentScript(): HasOne
|
||||
{
|
||||
return $this->hasOne(DeploymentScript::class);
|
||||
}
|
||||
|
||||
public function redirects(): HasMany
|
||||
{
|
||||
return $this->hasMany(Redirect::class);
|
||||
}
|
||||
|
||||
public function queues(): HasMany
|
||||
{
|
||||
return $this->hasMany(Queue::class);
|
||||
}
|
||||
|
||||
public function ssls(): HasMany
|
||||
{
|
||||
return $this->hasMany(Ssl::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
*/
|
||||
public function sourceControl(): SourceControl|HasOne|null
|
||||
{
|
||||
if (! $this->source_control) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->source_control == 'custom') {
|
||||
return new SourceControl([
|
||||
'user_id' => $this->id,
|
||||
'provider' => 'custom',
|
||||
'token' => '',
|
||||
'connected' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$sourceControl = SourceControl::query()->where('provider', $this->source_control)->first();
|
||||
|
||||
if (! $sourceControl) {
|
||||
throw new SourceControlIsNotConnected($this->source_control);
|
||||
}
|
||||
|
||||
return $sourceControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
*/
|
||||
public function getFullRepositoryUrlAttribute()
|
||||
{
|
||||
return $this->sourceControl()->provider()->fullRepoUrl($this->repository);
|
||||
}
|
||||
|
||||
public function getAliasesStringAttribute(): string
|
||||
{
|
||||
if (count($this->aliases) > 0) {
|
||||
return implode(' ', $this->aliases);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function type(): SiteType
|
||||
{
|
||||
$typeClass = config('core.site_types_class.'.$this->type);
|
||||
|
||||
return new $typeClass($this);
|
||||
}
|
||||
|
||||
public function install(): void
|
||||
{
|
||||
$this->type()->install();
|
||||
}
|
||||
|
||||
public function remove(): void
|
||||
{
|
||||
$this->update([
|
||||
'status' => SiteStatus::DELETING,
|
||||
]);
|
||||
$this->type()->delete();
|
||||
}
|
||||
|
||||
public function php(): ?Service
|
||||
{
|
||||
if ($this->php_version) {
|
||||
return $this->server->php($this->php_version);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function changePHPVersion($version): void
|
||||
{
|
||||
dispatch(new ChangePHPVersion($this, $version))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function getDeploymentScriptTextAttribute(): string
|
||||
{
|
||||
/* @var DeploymentScript $script */
|
||||
$script = $this->deploymentScript()->firstOrCreate([
|
||||
'site_id' => $this->id,
|
||||
], [
|
||||
'site_id' => $this->id,
|
||||
'name' => 'default',
|
||||
]);
|
||||
|
||||
return $script->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
*/
|
||||
public function deploy(): Deployment
|
||||
{
|
||||
if ($this->sourceControl()) {
|
||||
$this->sourceControl()->getRepo($this->repository);
|
||||
}
|
||||
|
||||
$deployment = new Deployment([
|
||||
'site_id' => $this->id,
|
||||
'deployment_script_id' => $this->deploymentScript->id,
|
||||
'status' => DeploymentStatus::DEPLOYING,
|
||||
]);
|
||||
$lastCommit = $this->sourceControl()->provider()->getLastCommit($this->repository, $this->branch);
|
||||
if ($lastCommit) {
|
||||
$deployment->commit_id = $lastCommit['commit_id'];
|
||||
$deployment->commit_data = $lastCommit['commit_data'];
|
||||
}
|
||||
$deployment->save();
|
||||
|
||||
dispatch(
|
||||
new Deploy(
|
||||
$deployment,
|
||||
$this->path,
|
||||
$this->deployment_script_text
|
||||
)
|
||||
)->onConnection('ssh');
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
||||
public function getEnvAttribute(): string
|
||||
{
|
||||
$typeData = $this->type_data;
|
||||
if (! isset($typeData['env'])) {
|
||||
$typeData['env'] = '';
|
||||
$this->type_data = $typeData;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return $typeData['env'];
|
||||
}
|
||||
|
||||
public function deployEnv(): void
|
||||
{
|
||||
dispatch(new DeployEnv($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function activeSsl(): HasOne
|
||||
{
|
||||
return $this->hasOne(Ssl::class)
|
||||
->where('expires_at', '>=', now())
|
||||
->orderByDesc('id');
|
||||
}
|
||||
|
||||
public function createFreeSsl(): void
|
||||
{
|
||||
$ssl = new Ssl([
|
||||
'site_id' => $this->id,
|
||||
'type' => 'letsencrypt',
|
||||
'expires_at' => now()->addMonths(3),
|
||||
'status' => SslStatus::CREATING,
|
||||
]);
|
||||
$ssl->save();
|
||||
$ssl->deploy();
|
||||
}
|
||||
|
||||
public function createCustomSsl(string $certificate, string $pk): void
|
||||
{
|
||||
$ssl = new Ssl([
|
||||
'site_id' => $this->id,
|
||||
'type' => 'custom',
|
||||
'certificate' => $certificate,
|
||||
'pk' => $pk,
|
||||
'expires_at' => '',
|
||||
'status' => SslStatus::CREATING,
|
||||
]);
|
||||
$ssl->save();
|
||||
$ssl->deploy();
|
||||
}
|
||||
|
||||
public function getUrlAttribute(): string
|
||||
{
|
||||
if ($this->activeSsl) {
|
||||
return 'https://'.$this->domain;
|
||||
}
|
||||
|
||||
return 'http://'.$this->domain;
|
||||
}
|
||||
|
||||
public function getWebDirectoryPathAttribute(): string
|
||||
{
|
||||
if ($this->web_directory) {
|
||||
return $this->path.'/'.$this->web_directory;
|
||||
}
|
||||
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceControlIsNotConnected
|
||||
* @throws ValidationException
|
||||
* @throws FailedToDeployGitHook
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function enableAutoDeployment(): void
|
||||
{
|
||||
if ($this->gitHook) {
|
||||
throw ValidationException::withMessages([
|
||||
'auto_deployment' => __('Auto deployment already enabled'),
|
||||
])->errorBag('auto_deployment');
|
||||
}
|
||||
|
||||
if (! $this->sourceControl()) {
|
||||
throw ValidationException::withMessages([
|
||||
'auto_deployment' => __('Your application does not use any source controls'),
|
||||
])->errorBag('auto_deployment');
|
||||
}
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$gitHook = new GitHook([
|
||||
'site_id' => $this->id,
|
||||
'source_control_id' => $this->sourceControl()->id,
|
||||
'secret' => generate_uid(),
|
||||
'actions' => ['deploy'],
|
||||
'events' => ['push'],
|
||||
]);
|
||||
$gitHook->save();
|
||||
$gitHook->deployHook();
|
||||
DB::commit();
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function disableAutoDeployment(): void
|
||||
{
|
||||
$this->gitHook?->destroyHook();
|
||||
}
|
||||
|
||||
public function getAutoDeploymentAttribute(): bool
|
||||
{
|
||||
return (bool) $this->gitHook;
|
||||
}
|
||||
|
||||
public function updateBranch(string $branch): void
|
||||
{
|
||||
dispatch(new UpdateBranch($this, $branch))->onConnection('ssh');
|
||||
}
|
||||
}
|
36
app/Models/SourceControl.php
Executable file
36
app/Models/SourceControl.php
Executable file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Contracts\SourceControlProvider;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property string $provider
|
||||
* @property string $access_token
|
||||
*/
|
||||
class SourceControl extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'access_token',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'access_token' => 'encrypted',
|
||||
];
|
||||
|
||||
public function provider(): SourceControlProvider
|
||||
{
|
||||
$providerClass = config('core.source_control_providers_class')[$this->provider];
|
||||
|
||||
return new $providerClass($this);
|
||||
}
|
||||
|
||||
public function getRepo(string $repo = null): ?array
|
||||
{
|
||||
return $this->provider()->getRepo($repo);
|
||||
}
|
||||
}
|
66
app/Models/SshKey.php
Normal file
66
app/Models/SshKey.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SshKeyStatus;
|
||||
use App\Jobs\SshKey\DeleteSshKeyFromServer;
|
||||
use App\Jobs\SshKey\DeploySshKeyToServer;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $name
|
||||
* @property string $public_key
|
||||
* @property User $user
|
||||
* @property Server[] $servers
|
||||
*/
|
||||
class SshKey extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'public_key',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'public_key' => 'encrypted',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function servers(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Server::class, 'server_ssh_keys')
|
||||
->withPivot('status')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
public function existsOnServer(Server $server): bool
|
||||
{
|
||||
return (bool) $this->servers()->where('id', $server->id)->first();
|
||||
}
|
||||
|
||||
public function deployTo(Server $server): void
|
||||
{
|
||||
$server->sshKeys()->attach($this, [
|
||||
'status' => SshKeyStatus::ADDING,
|
||||
]);
|
||||
dispatch(new DeploySshKeyToServer($server, $this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function deleteFrom(Server $server): void
|
||||
{
|
||||
$this->servers()->updateExistingPivot($server->id, [
|
||||
'status' => SshKeyStatus::DELETING,
|
||||
]);
|
||||
dispatch(new DeleteSshKeyFromServer($server, $this))->onConnection('ssh');
|
||||
}
|
||||
}
|
132
app/Models/Ssl.php
Normal file
132
app/Models/Ssl.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\SslStatus;
|
||||
use App\Jobs\Ssl\Deploy;
|
||||
use App\Jobs\Ssl\Remove;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property int $site_id
|
||||
* @property string $type
|
||||
* @property string $certificate
|
||||
* @property string $pk
|
||||
* @property string $ca
|
||||
* @property Carbon $expires_at
|
||||
* @property string $status
|
||||
* @property Site $site
|
||||
* @property string $certs_directory_path
|
||||
* @property string $certificate_path
|
||||
* @property string $pk_path
|
||||
* @property string $ca_path
|
||||
*/
|
||||
class Ssl extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'site_id',
|
||||
'type',
|
||||
'certificate',
|
||||
'pk',
|
||||
'ca',
|
||||
'expires_at',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'site_id' => 'integer',
|
||||
'certificate' => 'encrypted',
|
||||
'pk' => 'encrypted',
|
||||
'ca' => 'encrypted',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function site(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function getCertsDirectoryPathAttribute(): ?string
|
||||
{
|
||||
if ($this->type == 'letsencrypt') {
|
||||
return '/etc/letsencrypt/live/'.$this->site->domain;
|
||||
}
|
||||
|
||||
if ($this->type == 'custom') {
|
||||
return '/etc/ssl/'.$this->site->domain;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCertificatePathAttribute(): ?string
|
||||
{
|
||||
if ($this->type == 'letsencrypt') {
|
||||
return $this->certificate;
|
||||
}
|
||||
|
||||
if ($this->type == 'custom') {
|
||||
return $this->certs_directory_path.'/cert.pem';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getPkPathAttribute(): ?string
|
||||
{
|
||||
if ($this->type == 'letsencrypt') {
|
||||
return $this->pk;
|
||||
}
|
||||
|
||||
if ($this->type == 'custom') {
|
||||
return $this->certs_directory_path.'/privkey.pem';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCaPathAttribute(): ?string
|
||||
{
|
||||
if ($this->type == 'letsencrypt') {
|
||||
return $this->ca;
|
||||
}
|
||||
|
||||
if ($this->type == 'custom') {
|
||||
return $this->certs_directory_path.'/fullchain.pem';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function deploy(): void
|
||||
{
|
||||
dispatch(new Deploy($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function remove(): void
|
||||
{
|
||||
$this->status = SslStatus::DELETING;
|
||||
$this->save();
|
||||
dispatch(new Remove($this))->onConnection('ssh');
|
||||
}
|
||||
|
||||
public function validateSetup(string $result): bool
|
||||
{
|
||||
if (! Str::contains($result, 'Successfully received certificate')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->type == 'letsencrypt') {
|
||||
$this->certificate = $this->certs_directory_path.'/fullchain.pem';
|
||||
$this->pk = $this->certs_directory_path.'/privkey.pem';
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
50
app/Models/StorageProvider.php
Normal file
50
app/Models/StorageProvider.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $user_id
|
||||
* @property string $provider
|
||||
* @property string $label
|
||||
* @property string $token
|
||||
* @property string $refresh_token
|
||||
* @property bool $connected
|
||||
* @property Carbon $token_expires_at
|
||||
* @property User $user
|
||||
*/
|
||||
class StorageProvider extends AbstractModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'provider',
|
||||
'label',
|
||||
'token',
|
||||
'refresh_token',
|
||||
'connected',
|
||||
'token_expires_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'connected' => 'boolean',
|
||||
'token_expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function provider(): \App\Contracts\StorageProvider
|
||||
{
|
||||
$providerClass = config('core.storage_providers_class')[$this->provider];
|
||||
|
||||
return new $providerClass($this);
|
||||
}
|
||||
}
|
106
app/Models/User.php
Executable file
106
app/Models/User.php
Executable file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
* @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 string $profile_photo_url
|
||||
* @property string $timezone
|
||||
*/
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens;
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'timezone',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
'two_factor_recovery_codes',
|
||||
'two_factor_secret',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
];
|
||||
|
||||
public function sshKeys(): HasMany
|
||||
{
|
||||
return $this->hasMany(SshKey::class);
|
||||
}
|
||||
|
||||
public function sourceControls(): HasMany
|
||||
{
|
||||
return $this->hasMany(SourceControl::class);
|
||||
}
|
||||
|
||||
public function serverProviders(): HasMany
|
||||
{
|
||||
return $this->hasMany(ServerProvider::class);
|
||||
}
|
||||
|
||||
public function scripts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Script::class, 'user_id');
|
||||
}
|
||||
|
||||
public function sourceControl(string $provider): HasOne
|
||||
{
|
||||
return $this->hasOne(SourceControl::class)->where('provider', $provider);
|
||||
}
|
||||
|
||||
public function storageProviders(): HasMany
|
||||
{
|
||||
return $this->hasMany(StorageProvider::class);
|
||||
}
|
||||
|
||||
public function storageProvider(string $provider): HasOne
|
||||
{
|
||||
return $this->hasOne(StorageProvider::class)->where('provider', $provider);
|
||||
}
|
||||
|
||||
public function connectedStorageProviders(): HasMany
|
||||
{
|
||||
return $this->storageProviders()->where('connected', true);
|
||||
}
|
||||
|
||||
public function connectedSourceControls(): array
|
||||
{
|
||||
$connectedSourceControls = [];
|
||||
$sourceControls = $this->sourceControls()
|
||||
->where('connected', 1)
|
||||
->get(['provider']);
|
||||
foreach ($sourceControls as $sourceControl) {
|
||||
$connectedSourceControls[] = $sourceControl->provider;
|
||||
}
|
||||
|
||||
return $connectedSourceControls;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user