mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 06:17:36 +00:00
feat(backups): support scheduled backups for application storage targets
Add polymorphic volume backup scheduling for persistent volumes and directories, expose schedule management via API, and reorganize backup configuration and execution views.
This commit is contained in:
1 parent
995ec5fbb6
commit
d7385ad0c4
60 files changed
+3570
-985
No files matched your search
@@ -15,6 +15,10 @@ class Execution extends Component
|
||||
|
||||
public $s3s;
|
||||
|
||||
public array $parameters = [];
|
||||
|
||||
public string $section = 'general';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$backup_uuid = request()->route('backup_uuid');
|
||||
@@ -39,6 +43,14 @@ class Execution extends Component
|
||||
$this->backup = $backup;
|
||||
$this->executions = $executions;
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->section = match (request()->route()?->getName()) {
|
||||
'project.database.backup.s3' => 's3',
|
||||
'project.database.backup.retention' => 'retention',
|
||||
'project.database.backup.executions' => 'executions',
|
||||
'project.database.backup.danger' => 'danger',
|
||||
default => 'general',
|
||||
};
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -19,6 +19,8 @@ class BackupEdit extends Component
|
||||
|
||||
public ScheduledDatabaseBackup $backup;
|
||||
|
||||
public string $section = 'general';
|
||||
|
||||
#[Locked]
|
||||
public $availableS3Storages;
|
||||
|
||||
@@ -184,7 +186,11 @@ class BackupEdit extends Component
|
||||
'stack_service_uuid' => $serviceDatabase->uuid,
|
||||
]);
|
||||
} else {
|
||||
return redirect()->route('project.database.backup.index', $this->parameters);
|
||||
return redirect()->route('project.database.backup.index', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'environment_uuid' => $this->parameters['environment_uuid'],
|
||||
'database_uuid' => $this->parameters['database_uuid'],
|
||||
]);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->dispatch('error', 'Failed to delete backup: '.$e->getMessage());
|
||||
@@ -235,6 +241,19 @@ class BackupEdit extends Component
|
||||
$this->instantSave();
|
||||
}
|
||||
|
||||
public function toggleS3(): void
|
||||
{
|
||||
if (! $this->saveS3 && $this->availableS3StorageIds()->isEmpty()) {
|
||||
$this->dispatch('error', 'Select a usable S3 storage before enabling S3 backups.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->saveS3 = ! $this->saveS3;
|
||||
$this->disableLocalBackup = $this->saveS3 && $this->disableLocalBackup;
|
||||
$this->instantSave();
|
||||
}
|
||||
|
||||
private function customValidate()
|
||||
{
|
||||
if (! is_numeric($this->backup->s3_storage_id)) {
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\S3Storage;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
@@ -18,31 +16,11 @@ class CreateScheduledBackup extends Component
|
||||
#[Validate(['required', 'string'])]
|
||||
public $frequency;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $saveToS3 = false;
|
||||
|
||||
#[Locked]
|
||||
public $database;
|
||||
|
||||
public bool $enabled = true;
|
||||
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $s3StorageId = null;
|
||||
|
||||
public Collection $definedS3s;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->definedS3s = currentTeam()->s3s;
|
||||
if ($this->definedS3s->count() > 0) {
|
||||
$this->s3StorageId = $this->definedS3s->first()->id;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
@@ -50,20 +28,6 @@ class CreateScheduledBackup extends Component
|
||||
|
||||
$this->validate();
|
||||
|
||||
if ($this->saveToS3) {
|
||||
$s3StorageExists = ! is_null($this->s3StorageId)
|
||||
&& S3Storage::where('team_id', currentTeam()->id)
|
||||
->where('is_usable', true)
|
||||
->whereKey($this->s3StorageId)
|
||||
->exists();
|
||||
|
||||
if (! $s3StorageExists) {
|
||||
$this->dispatch('error', 'Please select a valid S3 storage to enable S3 backups.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$isValid = validate_cron_expression($this->frequency);
|
||||
if (! $isValid) {
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||
@@ -74,8 +38,8 @@ class CreateScheduledBackup extends Component
|
||||
$payload = [
|
||||
'enabled' => true,
|
||||
'frequency' => $this->frequency,
|
||||
'save_s3' => $this->saveToS3,
|
||||
's3_storage_id' => $this->s3StorageId,
|
||||
'save_s3' => false,
|
||||
's3_storage_id' => null,
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => $this->database->getMorphClass(),
|
||||
'team_id' => currentTeam()->id,
|
||||
@@ -91,11 +55,22 @@ class CreateScheduledBackup extends Component
|
||||
|
||||
$databaseBackup = ScheduledDatabaseBackup::create($payload);
|
||||
if ($this->database->getMorphClass() === ServiceDatabase::class) {
|
||||
$this->dispatch('refreshScheduledBackups', $databaseBackup->id);
|
||||
$service = $this->database->service;
|
||||
$this->redirectRoute('project.service.database.backup.show', [
|
||||
'project_uuid' => $service->project()->uuid,
|
||||
'environment_uuid' => $service->environment->uuid,
|
||||
'service_uuid' => $service->uuid,
|
||||
'stack_service_uuid' => $this->database->uuid,
|
||||
'backup_uuid' => $databaseBackup->uuid,
|
||||
], navigate: true);
|
||||
} else {
|
||||
$this->dispatch('refreshScheduledBackups');
|
||||
$this->redirectRoute('project.database.backup.execution', [
|
||||
'project_uuid' => $this->database->project()->uuid,
|
||||
'environment_uuid' => $this->database->environment->uuid,
|
||||
'database_uuid' => $this->database->uuid,
|
||||
'backup_uuid' => $databaseBackup->uuid,
|
||||
], navigate: true);
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
@@ -17,42 +16,18 @@ class ScheduledBackups extends Component
|
||||
|
||||
public $type;
|
||||
|
||||
public ?ScheduledDatabaseBackup $selectedBackup;
|
||||
|
||||
public $selectedBackupId;
|
||||
|
||||
public $s3s;
|
||||
|
||||
public string $custom_type = 'mysql';
|
||||
|
||||
protected $listeners = ['refreshScheduledBackups'];
|
||||
|
||||
protected $queryString = ['selectedBackupId'];
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
if ($this->selectedBackupId) {
|
||||
$this->setSelectedBackup($this->selectedBackupId, true);
|
||||
}
|
||||
$this->parameters = get_route_parameters();
|
||||
if ($this->database->getMorphClass() === ServiceDatabase::class) {
|
||||
$this->type = 'service-database';
|
||||
} else {
|
||||
$this->type = 'database';
|
||||
}
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
}
|
||||
|
||||
public function setSelectedBackup($backupId, $force = false)
|
||||
{
|
||||
if ($this->selectedBackupId === $backupId && ! $force) {
|
||||
return;
|
||||
}
|
||||
$this->selectedBackupId = $backupId;
|
||||
$this->selectedBackup = $this->database->scheduledBackups->find($backupId);
|
||||
if (is_null($this->selectedBackup)) {
|
||||
$this->selectedBackupId = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function setCustomType()
|
||||
@@ -86,9 +61,6 @@ class ScheduledBackups extends Component
|
||||
public function refreshScheduledBackups(?int $id = null): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
if ($id) {
|
||||
$this->setSelectedBackup($id);
|
||||
}
|
||||
$this->dispatch('refreshScheduledBackups');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user