mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-15 14:16:28 +00:00
feat(service): unify database and storage backups on one page
List service database schedules with volume backups, add a database picker when creating scheduled DB backups from the service page, and cover the unified table and create flow in tests.
This commit is contained in:
1 parent
12b14665b5
commit
c0b75d04a6
8 files changed
+288
-51
No files matched your search
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Attributes\Locked;
|
||||
@@ -17,16 +18,36 @@ class CreateScheduledBackup extends Component
|
||||
public $frequency;
|
||||
|
||||
#[Locked]
|
||||
public $database;
|
||||
public $database = null;
|
||||
|
||||
#[Locked]
|
||||
public ?Service $service = null;
|
||||
|
||||
public ?string $selectedDatabaseUuid = null;
|
||||
|
||||
public bool $enabled = true;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
if ($this->service) {
|
||||
$this->authorize('view', $this->service);
|
||||
$this->selectedDatabaseUuid = $this->availableDatabases()->first()?->uuid;
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->authorize('manageBackups', $this->database);
|
||||
$database = $this->selectedDatabase();
|
||||
if (! $database) {
|
||||
$this->addError('selectedDatabaseUuid', 'Select a database owned by this service.');
|
||||
|
||||
if (! $this->database->isBackupSolutionAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->authorize('manageBackups', $database);
|
||||
|
||||
if (! $database->isBackupSolutionAvailable()) {
|
||||
$this->dispatch('error', 'Scheduled backups are not supported for this database type.');
|
||||
|
||||
return;
|
||||
@@ -46,36 +67,36 @@ class CreateScheduledBackup extends Component
|
||||
'frequency' => $this->frequency,
|
||||
'save_s3' => false,
|
||||
's3_storage_id' => null,
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => $this->database->getMorphClass(),
|
||||
'database_id' => $database->id,
|
||||
'database_type' => $database->getMorphClass(),
|
||||
'team_id' => currentTeam()->id,
|
||||
];
|
||||
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||
} elseif ($this->database->type() === 'standalone-mysql') {
|
||||
$payload['databases_to_backup'] = $this->database->mysql_database;
|
||||
} elseif ($this->database->type() === 'standalone-mariadb') {
|
||||
$payload['databases_to_backup'] = $this->database->mariadb_database;
|
||||
} elseif ($this->database->type() === 'standalone-clickhouse') {
|
||||
$payload['databases_to_backup'] = $this->database->clickhouse_db;
|
||||
if ($database->type() === 'standalone-postgresql') {
|
||||
$payload['databases_to_backup'] = $database->postgres_db;
|
||||
} elseif ($database->type() === 'standalone-mysql') {
|
||||
$payload['databases_to_backup'] = $database->mysql_database;
|
||||
} elseif ($database->type() === 'standalone-mariadb') {
|
||||
$payload['databases_to_backup'] = $database->mariadb_database;
|
||||
} elseif ($database->type() === 'standalone-clickhouse') {
|
||||
$payload['databases_to_backup'] = $database->clickhouse_db;
|
||||
}
|
||||
|
||||
$databaseBackup = ScheduledDatabaseBackup::create($payload);
|
||||
if ($this->database->getMorphClass() === ServiceDatabase::class) {
|
||||
$service = $this->database->service;
|
||||
if ($database->getMorphClass() === ServiceDatabase::class) {
|
||||
$service = $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,
|
||||
'stack_service_uuid' => $database->uuid,
|
||||
'backup_uuid' => $databaseBackup->uuid,
|
||||
], navigate: true);
|
||||
} else {
|
||||
$this->redirectRoute('project.database.backup.execution', [
|
||||
'project_uuid' => $this->database->project()->uuid,
|
||||
'environment_uuid' => $this->database->environment->uuid,
|
||||
'database_uuid' => $this->database->uuid,
|
||||
'project_uuid' => $database->project()->uuid,
|
||||
'environment_uuid' => $database->environment->uuid,
|
||||
'database_uuid' => $database->uuid,
|
||||
'backup_uuid' => $databaseBackup->uuid,
|
||||
], navigate: true);
|
||||
}
|
||||
@@ -85,4 +106,32 @@ class CreateScheduledBackup extends Component
|
||||
$this->frequency = '';
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.create-scheduled-backup', [
|
||||
'databaseOptions' => $this->availableDatabases()->map(fn (ServiceDatabase $database): array => [
|
||||
'value' => $database->uuid,
|
||||
'label' => $database->human_name ?: $database->name,
|
||||
])->values()->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function availableDatabases()
|
||||
{
|
||||
if (! $this->service) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return $this->service->databases->filter(fn (ServiceDatabase $database): bool => $database->isBackupSolutionAvailable());
|
||||
}
|
||||
|
||||
private function selectedDatabase()
|
||||
{
|
||||
if (! $this->service) {
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
return $this->availableDatabases()->firstWhere('uuid', $this->selectedDatabaseUuid);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Livewire\Project\Service\VolumeBackup;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
@@ -30,6 +32,18 @@ class Index extends Component
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
$databaseTargets = $this->service->databases->filter(
|
||||
fn (ServiceDatabase $database): bool => $database->isBackupSolutionAvailable(),
|
||||
);
|
||||
|
||||
$databaseBackups = ScheduledDatabaseBackup::query()
|
||||
->with(['database', 'latest_log', 's3'])
|
||||
->withCount('executions')
|
||||
->where('database_type', (new ServiceDatabase)->getMorphClass())
|
||||
->whereHasMorph('database', [ServiceDatabase::class], fn ($query) => $query->where('service_id', $this->service->id))
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$backups = ScheduledVolumeBackup::query()
|
||||
->with(['backupable.resource', 'latestExecution', 's3'])
|
||||
->withCount('executions')
|
||||
@@ -37,7 +51,11 @@ class Index extends Component
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
return view('livewire.project.service.volume-backup.index', ['backups' => $backups]);
|
||||
return view('livewire.project.service.volume-backup.index', [
|
||||
'backups' => $backups,
|
||||
'databaseBackups' => $databaseBackups,
|
||||
'databaseTargets' => $databaseTargets,
|
||||
]);
|
||||
}
|
||||
|
||||
private function findService(): Service
|
||||
|
||||
Reference in new issue
Block a user