mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-15 20:15:45 +00:00
feat(backups): add scheduled persistent volume backups
This commit is contained in:
1 parent
05dc2c65f6
commit
63961e0799
36 files changed
+3506
-31
No files matched your search
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Application\Backup;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\S3Storage;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
#[Locked]
|
||||
public Application $application;
|
||||
|
||||
public ?int $selectedVolumeId = null;
|
||||
|
||||
public ?int $volumeId = null;
|
||||
|
||||
public bool $volumeLocked = false;
|
||||
|
||||
public string $frequency = 'daily';
|
||||
|
||||
public bool $saveToS3 = false;
|
||||
|
||||
public ?int $s3StorageId = null;
|
||||
|
||||
public Collection $volumes;
|
||||
|
||||
public Collection $definedS3s;
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'volumeId' => ['required', 'integer'],
|
||||
'frequency' => ['required', 'string'],
|
||||
'saveToS3' => ['required', 'boolean'],
|
||||
's3StorageId' => ['nullable', 'integer'],
|
||||
];
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->authorize('view', $this->application);
|
||||
$this->volumes = $this->application->persistentStorages()->orderBy('name')->get();
|
||||
$this->definedS3s = S3Storage::ownedByCurrentTeam()->where('is_usable', true)->get();
|
||||
$this->s3StorageId = $this->definedS3s->first()?->id;
|
||||
$this->volumeLocked = $this->selectedVolumeId !== null;
|
||||
$this->volumeId = $this->selectedVolumeId ?? $this->volumes->first()?->id;
|
||||
$this->loadSelectedBackup();
|
||||
}
|
||||
|
||||
public function updatedVolumeId(): void
|
||||
{
|
||||
$this->loadSelectedBackup();
|
||||
}
|
||||
|
||||
public function submit(): void
|
||||
{
|
||||
$this->authorize('update', $this->application);
|
||||
$this->validate();
|
||||
|
||||
$volume = $this->application->persistentStorages()->whereKey($this->volumeId)->first();
|
||||
if (! $volume) {
|
||||
$this->addError('volumeId', 'Select a volume owned by this application.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! validate_cron_expression($this->frequency)) {
|
||||
$this->addError('frequency', 'The frequency must be a valid cron or human expression.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->saveToS3 && ! $this->validS3StorageExists()) {
|
||||
$this->addError('s3StorageId', 'Select a usable S3 storage owned by your team.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$backup = ScheduledVolumeBackup::query()->updateOrCreate(
|
||||
['local_persistent_volume_id' => $volume->id],
|
||||
[
|
||||
'team_id' => currentTeam()->id,
|
||||
'frequency' => $this->frequency,
|
||||
'enabled' => true,
|
||||
'save_s3' => $this->saveToS3,
|
||||
's3_storage_id' => $this->saveToS3 ? $this->s3StorageId : null,
|
||||
],
|
||||
);
|
||||
|
||||
$this->dispatch('success', $backup->wasRecentlyCreated ? 'Scheduled volume backup created.' : 'Scheduled volume backup updated.');
|
||||
$this->dispatch('refreshVolumeBackups');
|
||||
$this->dispatch('close-modal');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.backup.create');
|
||||
}
|
||||
|
||||
private function loadSelectedBackup(): void
|
||||
{
|
||||
$volume = $this->volumes->firstWhere('id', $this->volumeId);
|
||||
if (! $volume) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backup = $volume->scheduledBackups()->first();
|
||||
if ($backup) {
|
||||
$this->frequency = $backup->frequency;
|
||||
$this->saveToS3 = $backup->save_s3;
|
||||
$this->s3StorageId = $backup->s3_storage_id ?? $this->definedS3s->first()?->id;
|
||||
}
|
||||
}
|
||||
|
||||
private function validS3StorageExists(): bool
|
||||
{
|
||||
return $this->s3StorageId !== null
|
||||
&& S3Storage::ownedByCurrentTeam()
|
||||
->where('is_usable', true)
|
||||
->whereKey($this->s3StorageId)
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Application\Backup;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Application $application;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
protected $listeners = ['refreshVolumeBackups' => '$refresh'];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->application = $this->findApplication();
|
||||
$this->authorize('view', $this->application);
|
||||
$this->parameters = get_route_parameters();
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$volumeIds = $this->application->persistentStorages()->pluck('id');
|
||||
$backups = ScheduledVolumeBackup::query()
|
||||
->with(['volume', 'latestExecution'])
|
||||
->withCount('executions')
|
||||
->whereIn('local_persistent_volume_id', $volumeIds)
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('livewire.project.application.backup.index', ['backups' => $backups]);
|
||||
}
|
||||
|
||||
private function findApplication(): Application
|
||||
{
|
||||
$project = currentTeam()->projects()
|
||||
->where('uuid', request()->route('project_uuid'))
|
||||
->firstOrFail();
|
||||
$environment = $project->environments()
|
||||
->where('uuid', request()->route('environment_uuid'))
|
||||
->firstOrFail();
|
||||
|
||||
return $environment->applications()
|
||||
->with('destination.server')
|
||||
->where('uuid', request()->route('application_uuid'))
|
||||
->firstOrFail();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project\Application\Backup;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Application $application;
|
||||
|
||||
public ScheduledVolumeBackup $backup;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$project = currentTeam()->projects()
|
||||
->where('uuid', request()->route('project_uuid'))
|
||||
->firstOrFail();
|
||||
$environment = $project->environments()
|
||||
->where('uuid', request()->route('environment_uuid'))
|
||||
->firstOrFail();
|
||||
$this->application = $environment->applications()
|
||||
->with('destination.server')
|
||||
->where('uuid', request()->route('application_uuid'))
|
||||
->firstOrFail();
|
||||
$this->authorize('view', $this->application);
|
||||
|
||||
$this->backup = ScheduledVolumeBackup::query()
|
||||
->with('volume')
|
||||
->where('uuid', request()->route('backup_uuid'))
|
||||
->whereIn('local_persistent_volume_id', $this->application->persistentStorages()->pluck('id'))
|
||||
->firstOrFail();
|
||||
$this->parameters = get_route_parameters();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.application.backup.show');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user