php-developer/app/Mod/Developer/Models/Server.php
2026-01-26 20:23:54 +00:00

182 lines
4.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Mod\Developer\Models;
use Core\Mod\Tenant\Concerns\BelongsToWorkspace;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
/**
* Server model for SSH connections.
*
* Stores server connection details with encrypted private key storage.
* Used with RemoteServerManager trait for remote command execution.
*
* @property int $id
* @property int $workspace_id
* @property string $name
* @property string $ip
* @property int $port
* @property string $user
* @property string|null $private_key
* @property string $status
* @property \Carbon\Carbon|null $last_connected_at
* @property \Carbon\Carbon|null $created_at
* @property \Carbon\Carbon|null $updated_at
* @property \Carbon\Carbon|null $deleted_at
*/
class Server extends Model
{
use BelongsToWorkspace;
use HasFactory;
use LogsActivity;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'workspace_id',
'name',
'ip',
'port',
'user',
'private_key',
'status',
'last_connected_at',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'private_key',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'port' => 'integer',
'last_connected_at' => 'datetime',
'private_key' => 'encrypted',
];
/**
* Default attribute values.
*
* @var array<string, mixed>
*/
protected $attributes = [
'port' => 22,
'user' => 'root',
'status' => 'pending',
];
/**
* Get the decrypted private key.
*
* Note: With the 'encrypted' cast, $this->private_key is automatically decrypted.
* This method provides a null-safe accessor.
*/
public function getDecryptedPrivateKey(): ?string
{
return $this->private_key;
}
/**
* Check if the server has a valid private key.
*/
public function hasPrivateKey(): bool
{
return $this->getDecryptedPrivateKey() !== null;
}
/**
* Get the server's connection string.
*/
public function getConnectionStringAttribute(): string
{
return "{$this->user}@{$this->ip}:{$this->port}";
}
/**
* Check if the server is connected.
*/
public function isConnected(): bool
{
return $this->status === 'connected';
}
/**
* Check if the server is in a failed state.
*/
public function isFailed(): bool
{
return $this->status === 'failed';
}
/**
* Mark the server as failed.
*/
public function markAsFailed(?string $reason = null): void
{
$this->update([
'status' => 'failed',
]);
if ($reason) {
activity()
->performedOn($this)
->withProperties(['reason' => $reason])
->log('Server connection failed');
}
}
/**
* Configure activity logging.
*/
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->logOnly(['name', 'ip', 'port', 'user', 'status'])
->logOnlyDirty()
->dontSubmitEmptyLogs();
}
/**
* Scope to only connected servers.
*/
public function scopeConnected(Builder $query): Builder
{
return $query->where('status', 'connected');
}
/**
* Scope to only failed servers.
*/
public function scopeFailed(Builder $query): Builder
{
return $query->where('status', 'failed');
}
/**
* Scope to only pending servers.
*/
public function scopePending(Builder $query): Builder
{
return $query->where('status', 'pending');
}
}