Files
jabali-panel/app/Models/Backup.php
2026-02-02 03:11:45 +02:00

263 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Backup extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'destination_id',
'schedule_id',
'name',
'filename',
'type',
'include_files',
'include_databases',
'include_mailboxes',
'include_dns',
'domains',
'databases',
'mailboxes',
'users',
'size_bytes',
'file_count',
'status',
'local_path',
'remote_path',
'checksum',
'started_at',
'completed_at',
'error_message',
'metadata',
'expires_at',
];
protected function casts(): array
{
return [
'include_files' => 'boolean',
'include_databases' => 'boolean',
'include_mailboxes' => 'boolean',
'include_dns' => 'boolean',
'domains' => 'array',
'databases' => 'array',
'mailboxes' => 'array',
'users' => 'array',
'size_bytes' => 'integer',
'file_count' => 'integer',
'metadata' => 'array',
'started_at' => 'datetime',
'completed_at' => 'datetime',
'expires_at' => 'datetime',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function destination(): BelongsTo
{
return $this->belongsTo(BackupDestination::class, 'destination_id');
}
public function schedule(): BelongsTo
{
return $this->belongsTo(BackupSchedule::class, 'schedule_id');
}
public function restores(): HasMany
{
return $this->hasMany(BackupRestore::class);
}
/**
* Get human-readable file size.
*/
public function getSizeHumanAttribute(): string
{
$bytes = $this->size_bytes;
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
}
return $bytes . ' bytes';
}
/**
* Get backup duration in human-readable format.
*/
public function getDurationAttribute(): ?string
{
if (!$this->started_at || !$this->completed_at) {
return null;
}
$seconds = $this->completed_at->diffInSeconds($this->started_at);
if ($seconds >= 3600) {
return gmdate('H:i:s', $seconds);
} elseif ($seconds >= 60) {
return gmdate('i:s', $seconds) . ' min';
}
return $seconds . ' sec';
}
/**
* Check if backup has expired.
*/
public function getIsExpiredAttribute(): bool
{
return $this->expires_at && $this->expires_at->isPast();
}
/**
* Check if backup is a server-wide backup.
*/
public function isServerBackup(): bool
{
return $this->type === 'server';
}
/**
* Check if backup is stored locally.
*/
public function isLocal(): bool
{
return $this->destination_id === null || $this->destination?->isLocal();
}
/**
* Check if backup is stored remotely.
*/
public function isRemote(): bool
{
return $this->destination && $this->destination->isRemote();
}
/**
* Check if backup can be downloaded directly.
*/
public function canDownload(): bool
{
return $this->status === 'completed' && $this->local_path && file_exists($this->local_path);
}
/**
* Get the download path for the backup.
*/
public function getDownloadPath(): ?string
{
if ($this->canDownload()) {
return $this->local_path;
}
return null;
}
/**
* Scope for completed backups.
*/
public function scopeCompleted($query)
{
return $query->where('status', 'completed');
}
/**
* Scope for failed backups.
*/
public function scopeFailed($query)
{
return $query->where('status', 'failed');
}
/**
* Scope for running backups.
*/
public function scopeRunning($query)
{
return $query->whereIn('status', ['pending', 'running', 'uploading']);
}
/**
* Scope for user backups.
*/
public function scopeForUser($query, int $userId)
{
return $query->where('user_id', $userId);
}
/**
* Scope for server backups.
*/
public function scopeServerBackups($query)
{
return $query->where('type', 'server');
}
/**
* Scope for non-expired backups.
*/
public function scopeNotExpired($query)
{
return $query->where(function ($q) {
$q->whereNull('expires_at')
->orWhere('expires_at', '>', now());
});
}
/**
* Scope for expired backups.
*/
public function scopeExpired($query)
{
return $query->whereNotNull('expires_at')
->where('expires_at', '<=', now());
}
/**
* Get status badge color for UI.
*/
public function getStatusColorAttribute(): string
{
return match ($this->status) {
'completed' => 'success',
'failed' => 'danger',
'running', 'uploading' => 'warning',
'pending' => 'gray',
default => 'gray',
};
}
/**
* Get status label for UI.
*/
public function getStatusLabelAttribute(): string
{
return match ($this->status) {
'pending' => 'Pending',
'running' => 'Creating...',
'uploading' => 'Uploading...',
'completed' => 'Completed',
'failed' => 'Failed',
default => ucfirst($this->status),
};
}
}